From 0ca7a50e96f8ff596de7432462a5e92a98de5e38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:50:28 +0100 Subject: [PATCH 01/73] Update devDependencies (non-major) (#28200) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 142 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 40 deletions(-) diff --git a/yarn.lock b/yarn.lock index ad792d26c..3ec023533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2893,8 +2893,8 @@ __metadata: linkType: hard "@testing-library/jest-dom@npm:^6.0.0": - version: 6.1.4 - resolution: "@testing-library/jest-dom@npm:6.1.4" + version: 6.1.5 + resolution: "@testing-library/jest-dom@npm:6.1.5" dependencies: "@adobe/css-tools": "npm:^4.3.1" "@babel/runtime": "npm:^7.9.2" @@ -2918,7 +2918,7 @@ __metadata: optional: true vitest: optional: true - checksum: 2e23f120613fd8ae6d5169bbc94f1a2e4c82b07182057dc94db8ec54ebf32555833442e6c43a187e59715d83704ffb5df49ba88a71f6f32d2683f3d95ba721c7 + checksum: f3643a56fcd970b5c7e8fd10faf3c4817d8ab0e74fb1198d726643bdc5ac675ceaac3b0068c5b4fbad254470e8f98ed50028741de875a29ceaa2f854570979c9 languageName: node linkType: hard @@ -4168,12 +4168,12 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^5.0.0": - version: 5.0.0 - resolution: "ansi-escapes@npm:5.0.0" +"ansi-escapes@npm:^6.2.0": + version: 6.2.0 + resolution: "ansi-escapes@npm:6.2.0" dependencies: - type-fest: "npm:^1.0.2" - checksum: f705cc7fbabb981ddf51562cd950792807bccd7260cc3d9478a619dda62bff6634c87ca100f2545ac7aade9b72652c4edad8c7f0d31a0b949b5fa58f33eaf0d0 + type-fest: "npm:^3.0.0" + checksum: 3eec75deedd8b10192c5f98e4cd9715cc3ff268d33fc463c24b7d22446668bfcd4ad1803993ea89c0f51f88b5a3399572bacb7c8cb1a067fc86e189c5f3b0c7e languageName: node linkType: hard @@ -4239,7 +4239,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0": +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" checksum: 5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c @@ -5584,13 +5584,13 @@ __metadata: languageName: node linkType: hard -"cli-truncate@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-truncate@npm:3.1.0" +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" dependencies: slice-ansi: "npm:^5.0.0" - string-width: "npm:^5.0.0" - checksum: a19088878409ec0e5dc2659a5166929629d93cfba6d68afc9cde2282fd4c751af5b555bf197047e31c87c574396348d011b7aa806fec29c4139ea4f7f00b324c + string-width: "npm:^7.0.0" + checksum: d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c languageName: node linkType: hard @@ -6997,7 +6997,7 @@ __metadata: languageName: node linkType: hard -"emoji-regex@npm:10.3.0, emoji-regex@npm:^10.2.1": +"emoji-regex@npm:10.3.0, emoji-regex@npm:^10.2.1, emoji-regex@npm:^10.3.0": version: 10.3.0 resolution: "emoji-regex@npm:10.3.0" checksum: b4838e8dcdceb44cf47f59abe352c25ff4fe7857acaf5fb51097c427f6f75b44d052eb907a7a3b86f86bc4eae3a93f5c2b7460abe79c407307e6212d65c91163 @@ -8387,6 +8387,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.2.0 + resolution: "get-east-asian-width@npm:1.2.0" + checksum: 914b1e217cf38436c24b4c60b4c45289e39a45bf9e65ef9fd343c2815a1a02b8a0215aeec8bf9c07c516089004b6e3826332481f40a09529fcadbf6e579f286b + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": version: 1.2.2 resolution: "get-intrinsic@npm:1.2.2" @@ -9610,6 +9617,15 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^5.0.0": + version: 5.0.0 + resolution: "is-fullwidth-code-point@npm:5.0.0" + dependencies: + get-east-asian-width: "npm:^1.0.0" + checksum: cd591b27d43d76b05fa65ed03eddce57a16e1eca0b7797ff7255de97019bcaf0219acfc0c4f7af13319e13541f2a53c0ace476f442b13267b9a6a7568f2b65c8 + languageName: node + linkType: hard + "is-generator-fn@npm:^2.0.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" @@ -10892,7 +10908,14 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:2.1.0, lilconfig@npm:^2.1.0": +"lilconfig@npm:3.0.0": + version: 3.0.0 + resolution: "lilconfig@npm:3.0.0" + checksum: 7f5ee7a658dc016cacf146815e8d88b06f06f4402823b8b0934e305a57a197f55ccc9c5cd4fb5ea1b2b821c8ccaf2d54abd59602a4931af06eabda332388d3e6 + languageName: node + linkType: hard + +"lilconfig@npm:^2.1.0": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" checksum: 64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 @@ -10907,36 +10930,36 @@ __metadata: linkType: hard "lint-staged@npm:^15.0.0": - version: 15.1.0 - resolution: "lint-staged@npm:15.1.0" + version: 15.2.0 + resolution: "lint-staged@npm:15.2.0" dependencies: chalk: "npm:5.3.0" commander: "npm:11.1.0" debug: "npm:4.3.4" execa: "npm:8.0.1" - lilconfig: "npm:2.1.0" - listr2: "npm:7.0.2" + lilconfig: "npm:3.0.0" + listr2: "npm:8.0.0" micromatch: "npm:4.0.5" pidtree: "npm:0.6.0" string-argv: "npm:0.3.2" yaml: "npm:2.3.4" bin: lint-staged: bin/lint-staged.js - checksum: d427408be98df7558e918593cb765d5caaa67a5cdca89671fb54280a6c959f4e448db36d4f85e8e0bd9c2c1e996aa133916925cf47c9df573b47308d5e298d84 + checksum: 4a1ff25dd06dbd4346fd244c9a0ebb936532ba18c0caedeb895c2e232f3c6c5fd08f6667624716660bc29e3e0f9f0440a9175114394616e991ebd5fab4b1f092 languageName: node linkType: hard -"listr2@npm:7.0.2": - version: 7.0.2 - resolution: "listr2@npm:7.0.2" +"listr2@npm:8.0.0": + version: 8.0.0 + resolution: "listr2@npm:8.0.0" dependencies: - cli-truncate: "npm:^3.1.0" + cli-truncate: "npm:^4.0.0" colorette: "npm:^2.0.20" eventemitter3: "npm:^5.0.1" - log-update: "npm:^5.0.1" + log-update: "npm:^6.0.0" rfdc: "npm:^1.3.0" - wrap-ansi: "npm:^8.1.0" - checksum: 37b6501be84ebea66dcce07c5f86c224aff0c01c9fb43f5055cc38a063030281d58198aad0aad481f174438309831ddf5f763b890e820cd7b7b4f4a5dfa229c9 + wrap-ansi: "npm:^9.0.0" + checksum: 6e356df9127c68b69186c927c993645223557e941a76b0bb210e35786aedc53f577df437251db804606ff37ac509c5d945289a84b3daee7fadf2e3dcb889ecc9 languageName: node linkType: hard @@ -11104,16 +11127,16 @@ __metadata: languageName: node linkType: hard -"log-update@npm:^5.0.1": - version: 5.0.1 - resolution: "log-update@npm:5.0.1" +"log-update@npm:^6.0.0": + version: 6.0.0 + resolution: "log-update@npm:6.0.0" dependencies: - ansi-escapes: "npm:^5.0.0" + ansi-escapes: "npm:^6.2.0" cli-cursor: "npm:^4.0.0" - slice-ansi: "npm:^5.0.0" - strip-ansi: "npm:^7.0.1" - wrap-ansi: "npm:^8.0.1" - checksum: 1050ea2027e80f32e132aace909987cb00c2719368c78b82ffca681a5b3f4020eeb5f4b4e310c47c35c6c36aff258c1d1bc51485ac44d6fdac9eb0a4275c539f + slice-ansi: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: e0b3c3401ef49ce3eb17e2f83d644765e4f7988498fc1344eaa4f31ab30e510dcc469a7fb64dc01bd1c8d9237d917598fa677a9818705fb3774c10f6e9d4b27c languageName: node linkType: hard @@ -15050,6 +15073,16 @@ __metadata: languageName: node linkType: hard +"slice-ansi@npm:^7.0.0": + version: 7.1.0 + resolution: "slice-ansi@npm:7.1.0" + dependencies: + ansi-styles: "npm:^6.2.1" + is-fullwidth-code-point: "npm:^5.0.0" + checksum: 631c971d4abf56cf880f034d43fcc44ff883624867bf11ecbd538c47343911d734a4656d7bc02362b40b89d765652a7f935595441e519b59e2ad3f4d5d6fe7ca + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -15493,7 +15526,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^5.0.0, string-width@npm:^5.0.1, string-width@npm:^5.1.2": +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": version: 5.1.2 resolution: "string-width@npm:5.1.2" dependencies: @@ -15504,6 +15537,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0": + version: 7.0.0 + resolution: "string-width@npm:7.0.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 8ffaeeccf4a56ccce5b6235d0b99ee3a581e3e3e5d453708efe7aa8e264fa3a858b4fe2244310cb71c6a20d8c05921cedc8b2ccd88cbaad9f5c92051ff68edc6 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.6, string.prototype.matchall@npm:^4.0.8": version: 4.0.8 resolution: "string.prototype.matchall@npm:4.0.8" @@ -15618,7 +15662,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -16379,13 +16423,20 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^1.0.1, type-fest@npm:^1.0.2, type-fest@npm:^1.2.1, type-fest@npm:^1.2.2": +"type-fest@npm:^1.0.1, type-fest@npm:^1.2.1, type-fest@npm:^1.2.2": version: 1.4.0 resolution: "type-fest@npm:1.4.0" checksum: a3c0f4ee28ff6ddf800d769eafafcdeab32efa38763c1a1b8daeae681920f6e345d7920bf277245235561d8117dab765cb5f829c76b713b4c9de0998a5397141 languageName: node linkType: hard +"type-fest@npm:^3.0.0": + version: 3.13.1 + resolution: "type-fest@npm:3.13.1" + checksum: 547d22186f73a8c04590b70dcf63baff390078c75ea8acd366bbd510fd0646e348bd1970e47ecf795b7cff0b41d26e9c475c1fedd6ef5c45c82075fbf916b629 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -17613,7 +17664,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^8.0.1, wrap-ansi@npm:^8.1.0": +"wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" dependencies: @@ -17624,6 +17675,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.0 + resolution: "wrap-ansi@npm:9.0.0" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: a139b818da9573677548dd463bd626a5a5286271211eb6e4e82f34a4f643191d74e6d4a9bb0a3c26ec90e6f904f679e0569674ac099ea12378a8b98e20706066 + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" From b4fef6c26fe63aeda60a6359a17a7f357b2c923e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:50:43 +0100 Subject: [PATCH 02/73] Update DefinitelyTyped types (non-major) (#28199) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3ec023533..288d75925 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3495,13 +3495,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": - version: 18.2.38 - resolution: "@types/react@npm:18.2.38" + version: 18.2.41 + resolution: "@types/react@npm:18.2.41" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 56edd4756081b677e38ee23ad6d340658c5e2468785cb20968318cec357e1ea7ccf3ecd9534981741192dd1b894200acfaf0f1551b4795c6077668f6afc19345 + checksum: 5cc72491ce8be95e7bbedd8bf039ca971772ecd22d989feb045af7e73247c7e6cff25a2f1c2200be461fb2f6b5aacef739e1ba9fd83c744209dfd3ce8aa75afe languageName: node linkType: hard @@ -3657,11 +3657,11 @@ __metadata: linkType: hard "@types/ws@npm:^8.5.9": - version: 8.5.9 - resolution: "@types/ws@npm:8.5.9" + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" dependencies: "@types/node": "npm:*" - checksum: 678bdd6461c4653f2975c537fb673cb1918c331558e2d2422b69761c9ced67200bb07c664e2593f3864077a891cb7c13ef2a40d303b4aacb06173d095d8aa3ce + checksum: e9af279b984c4a04ab53295a40aa95c3e9685f04888df5c6920860d1dd073fcc57c7bd33578a04b285b2c655a0b52258d34bee0a20569dca8defb8393e1e5d29 languageName: node linkType: hard From 3ec263bf155c87dd5c513f181682e051e9f3e105 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:51:08 +0100 Subject: [PATCH 03/73] Update dependency irb to v1.10.0 (#28198) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4c78e6b0a..6820c12f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -377,7 +377,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.5) io-console (0.6.0) - irb (1.9.1) + irb (1.10.0) rdoc reline (>= 0.3.8) jmespath (1.6.2) @@ -747,7 +747,7 @@ GEM statsd-ruby (1.5.0) stoplight (3.0.2) redlock (~> 1.0) - stringio (3.0.9) + stringio (3.1.0) strong_migrations (1.6.4) activerecord (>= 5.2) swd (1.3.0) From d848d8d87cbef49f5f4635b3378b582464bae98a Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 03:52:21 -0500 Subject: [PATCH 04/73] Add helper methods for domains allow and export blocks files (#28196) --- .../admin/export_domain_allows_controller_spec.rb | 10 ++++++++-- .../admin/export_domain_blocks_controller_spec.rb | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/spec/controllers/admin/export_domain_allows_controller_spec.rb b/spec/controllers/admin/export_domain_allows_controller_spec.rb index e1e5ecc1f..0a2e34262 100644 --- a/spec/controllers/admin/export_domain_allows_controller_spec.rb +++ b/spec/controllers/admin/export_domain_allows_controller_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Admin::ExportDomainAllowsController do get :export, params: { format: :csv } expect(response).to have_http_status(200) - expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_allows.csv'))) + expect(response.body).to eq(domain_allows_csv_file) end end @@ -40,7 +40,7 @@ RSpec.describe Admin::ExportDomainAllowsController do # Domains should now be added get :export, params: { format: :csv } expect(response).to have_http_status(200) - expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_allows.csv'))) + expect(response.body).to eq(domain_allows_csv_file) end it 'displays error on no file selected' do @@ -49,4 +49,10 @@ RSpec.describe Admin::ExportDomainAllowsController do expect(flash[:error]).to eq(I18n.t('admin.export_domain_allows.no_file')) end end + + private + + def domain_allows_csv_file + File.read(File.join(file_fixture_path, 'domain_allows.csv')) + end end diff --git a/spec/controllers/admin/export_domain_blocks_controller_spec.rb b/spec/controllers/admin/export_domain_blocks_controller_spec.rb index 5a282c957..bfcccfa06 100644 --- a/spec/controllers/admin/export_domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/export_domain_blocks_controller_spec.rb @@ -26,7 +26,13 @@ RSpec.describe Admin::ExportDomainBlocksController do get :export, params: { format: :csv } expect(response).to have_http_status(200) - expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_blocks.csv'))) + expect(response.body).to eq(domain_blocks_csv_file) + end + + private + + def domain_blocks_csv_file + File.read(File.join(file_fixture_path, 'domain_blocks.csv')) end end From 154fb95e44da94d8473ecdc081cf8b9648f41a52 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:52:53 +0100 Subject: [PATCH 05/73] Update dependency postcss to v8.4.32 (#28185) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 288d75925..4226fcc0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11784,12 +11784,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.6": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: nanoid: bin/nanoid.cjs - checksum: 606b355960d0fcbe3d27924c4c52ef7d47d3b57208808ece73279420d91469b01ec1dce10fae512b6d4a8c5a5432b352b228336a8b2202a6ea68e67fa348e2ee + checksum: e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 languageName: node linkType: hard @@ -13218,13 +13218,13 @@ __metadata: linkType: hard "postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.28": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" + version: 8.4.32 + resolution: "postcss@npm:8.4.32" dependencies: - nanoid: "npm:^3.3.6" + nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" source-map-js: "npm:^1.0.2" - checksum: 748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + checksum: 39308a9195fa34d4dbdd7b58a896cff0c7809f84f7a4ac1b95b68ca86c9138a395addff33075668ed3983d41b90aac05754c445237a9365eb1c3a5602ebd03ad languageName: node linkType: hard From 19ad51253d255c26ba71b0754c8b21197fcd0113 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 04:02:40 -0500 Subject: [PATCH 06/73] Prevent triple-subject run in admin/domain_blocks spec (#28195) --- .../admin/domain_blocks_controller_spec.rb | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index 13826be36..22960f531 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -192,16 +192,11 @@ RSpec.describe Admin::DomainBlocksController do let(:original_severity) { 'suspend' } let(:new_severity) { 'silence' } - it 'changes the block severity' do - expect { subject }.to change { domain_block.reload.severity }.from('suspend').to('silence') - end - - it 'undoes individual suspensions' do - expect { subject }.to change { remote_account.reload.suspended? }.from(true).to(false) - end - - it 'performs individual silences' do - expect { subject }.to change { remote_account.reload.silenced? }.from(false).to(true) + it 'changes the block severity, suspensions, and silences' do + expect { subject } + .to change_severity('suspend', 'silence') + .and change_suspended(true, false) + .and change_silenced(false, true) end end @@ -209,17 +204,26 @@ RSpec.describe Admin::DomainBlocksController do let(:original_severity) { 'silence' } let(:new_severity) { 'suspend' } - it 'changes the block severity' do - expect { subject }.to change { domain_block.reload.severity }.from('silence').to('suspend') + it 'changes the block severity, silences, and suspensions' do + expect { subject } + .to change_severity('silence', 'suspend') + .and change_silenced(true, false) + .and change_suspended(false, true) end + end - it 'undoes individual silences' do - expect { subject }.to change { remote_account.reload.silenced? }.from(true).to(false) - end + private - it 'performs individual suspends' do - expect { subject }.to change { remote_account.reload.suspended? }.from(false).to(true) - end + def change_severity(from, to) + change { domain_block.reload.severity }.from(from).to(to) + end + + def change_silenced(from, to) + change { remote_account.reload.silenced? }.from(from).to(to) + end + + def change_suspended(from, to) + change { remote_account.reload.suspended? }.from(from).to(to) end end From 1bf2230fd1a25a1f8c012e4ff70c08a0a48d88d3 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 04:08:47 -0500 Subject: [PATCH 07/73] Add spec coverage for `CLI::Upgrade#storage_schema` command (#28180) --- spec/lib/mastodon/cli/upgrade_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/lib/mastodon/cli/upgrade_spec.rb b/spec/lib/mastodon/cli/upgrade_spec.rb index 817044f7e..0d6494eee 100644 --- a/spec/lib/mastodon/cli/upgrade_spec.rb +++ b/spec/lib/mastodon/cli/upgrade_spec.rb @@ -4,5 +4,24 @@ require 'rails_helper' require 'mastodon/cli/upgrade' describe Mastodon::CLI::Upgrade do + let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + + describe '#storage_schema' do + context 'with records that dont need upgrading' do + let(:options) { {} } + + before do + Fabricate(:account) + Fabricate(:media_attachment) + end + + it 'does not upgrade storage for the attachments' do + expect { cli.invoke(:storage_schema, [], options) }.to output( + a_string_including('Upgraded storage schema of 0 records') + ).to_stdout + end + end + end end From 9603198982fce7e064a4ec60ac36420173a91cd5 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 04:09:05 -0500 Subject: [PATCH 08/73] Add spec coverage for `CLI::Domains#purge` command (#28179) --- spec/lib/mastodon/cli/domains_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/lib/mastodon/cli/domains_spec.rb b/spec/lib/mastodon/cli/domains_spec.rb index 765b63e2a..add754159 100644 --- a/spec/lib/mastodon/cli/domains_spec.rb +++ b/spec/lib/mastodon/cli/domains_spec.rb @@ -4,5 +4,22 @@ require 'rails_helper' require 'mastodon/cli/domains' describe Mastodon::CLI::Domains do + let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + + describe '#purge' do + context 'with accounts from the domain' do + let(:options) { {} } + let(:domain) { 'host.example' } + let!(:account) { Fabricate(:account, domain: domain) } + + it 'removes the account' do + expect { cli.invoke(:purge, [domain], options) }.to output( + a_string_including('Removed 1 accounts') + ).to_stdout + expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end end From a2bcfeb887a0ae1d437bb727333d769a8248b578 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Mon, 4 Dec 2023 10:09:43 +0100 Subject: [PATCH 09/73] Fix `Style/HashEachMethods` cop in HAML files (#28178) --- app/views/admin/reports/index.html.haml | 2 +- app/views/settings/applications/_fields.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index e94847d67..e2a9868aa 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -27,7 +27,7 @@ %button.button= t('admin.accounts.search') = link_to t('admin.accounts.reset'), admin_reports_path, class: 'button negative' -- @reports.group_by(&:target_account_id).each do |_target_account_id, reports| +- @reports.group_by(&:target_account_id).each_value do |reports| - target_account = reports.first.target_account .report-card .report-card__profile diff --git a/app/views/settings/applications/_fields.html.haml b/app/views/settings/applications/_fields.html.haml index 4f5077d83..29e2bcb5a 100644 --- a/app/views/settings/applications/_fields.html.haml +++ b/app/views/settings/applications/_fields.html.haml @@ -14,5 +14,5 @@ %label= t('activerecord.attributes.doorkeeper/application.scopes') %span.hint= t('simple_form.hints.defaults.scopes') - - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each do |_key, value| + - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value| = f.input :scopes, label: false, hint: false, collection: value.sort, wrapper: :with_block_label, include_blank: false, label_method: ->(scope) { safe_join([content_tag(:samp, scope, class: class_for_scope(scope)), content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) }, selected: f.object.scopes.all, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' From 829457212ee2ae933db273b8dce402d2d8e659cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:38:49 +0100 Subject: [PATCH 10/73] Update dependency rubocop to v1.58.0 (#28170) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6820c12f9..037fe145d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -381,7 +381,7 @@ GEM rdoc reline (>= 0.3.8) jmespath (1.6.2) - json (2.6.3) + json (2.7.0) json-canonicalization (0.3.2) json-jwt (1.15.3) activesupport (>= 4.2) @@ -656,7 +656,7 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 8) rspec-support (3.12.1) - rubocop (1.57.2) + rubocop (1.58.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -664,7 +664,7 @@ GEM rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.30.0) From b3b009e6aa29d9051aa61113da344b3ab26d98e4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 04:44:54 -0500 Subject: [PATCH 11/73] Add spec coverage for `CLI::EmailDomainBlocks` commands (#28181) --- lib/mastodon/cli/email_domain_blocks.rb | 4 +- .../mastodon/cli/email_domain_blocks_spec.rb | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/lib/mastodon/cli/email_domain_blocks.rb b/lib/mastodon/cli/email_domain_blocks.rb index 88a84ecb4..022b1dcbb 100644 --- a/lib/mastodon/cli/email_domain_blocks.rb +++ b/lib/mastodon/cli/email_domain_blocks.rb @@ -7,10 +7,10 @@ module Mastodon::CLI class EmailDomainBlocks < Base desc 'list', 'List blocked e-mail domains' def list - EmailDomainBlock.where(parent_id: nil).order(id: 'DESC').find_each do |entry| + EmailDomainBlock.where(parent_id: nil).find_each do |entry| say(entry.domain.to_s, :white) - EmailDomainBlock.where(parent_id: entry.id).order(id: 'DESC').find_each do |child| + EmailDomainBlock.where(parent_id: entry.id).find_each do |child| say(" #{child.domain}", :cyan) end end diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index 060943b18..f5cb6c332 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -4,5 +4,98 @@ require 'rails_helper' require 'mastodon/cli/email_domain_blocks' describe Mastodon::CLI::EmailDomainBlocks do + let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + + describe '#list' do + context 'with email domain block records' do + let!(:parent_block) { Fabricate(:email_domain_block) } + let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) } + let(:options) { {} } + + it 'lists the blocks' do + expect { cli.invoke(:list, [], options) }.to output( + a_string_including(parent_block.domain) + .and(a_string_including(child_block.domain)) + ).to_stdout + end + end + end + + describe '#add' do + context 'without any options' do + let(:options) { {} } + + it 'warns about usage and exits' do + expect { cli.invoke(:add, [], options) }.to output( + a_string_including('No domain(s) given') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'when blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + before { Fabricate(:email_domain_block, domain: domain) } + + it 'does not add a new block' do + expect { cli.invoke(:add, [domain], options) }.to output( + a_string_including('is already blocked') + ).to_stdout + .and(not_change(EmailDomainBlock, :count)) + end + end + + context 'when no blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + it 'adds a new block' do + expect { cli.invoke(:add, [domain], options) }.to output( + a_string_including('Added 1') + ).to_stdout + .and(change(EmailDomainBlock, :count).by(1)) + end + end + end + + describe '#remove' do + context 'without any options' do + let(:options) { {} } + + it 'warns about usage and exits' do + expect { cli.invoke(:remove, [], options) }.to output( + a_string_including('No domain(s) given') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'when blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + before { Fabricate(:email_domain_block, domain: domain) } + + it 'removes the block' do + expect { cli.invoke(:remove, [domain], options) }.to output( + a_string_including('Removed 1') + ).to_stdout + .and(change(EmailDomainBlock, :count).by(-1)) + end + end + + context 'when no blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + it 'does not remove a block' do + expect { cli.invoke(:remove, [domain], options) }.to output( + a_string_including('is not yet blocked') + ).to_stdout + .and(not_change(EmailDomainBlock, :count)) + end + end + end end From cca19f5fbb568bf7f145fe98d6d2497632c8987c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 07:56:28 -0500 Subject: [PATCH 12/73] Use the `Admin::ActionLog` fabricator in admin/action_logs spec (#28194) --- .../admin/accounts_controller_spec.rb | 32 ++++++++++++------- .../admin/action_logs_controller_spec.rb | 31 +++++++++++++++--- .../api/v1/admin/account_actions_spec.rb | 17 +++++++--- spec/requests/api/v1/admin/accounts_spec.rb | 32 ++++++++++++------- 4 files changed, 78 insertions(+), 34 deletions(-) diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index 307e81950..1882ea838 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -161,12 +161,13 @@ RSpec.describe Admin::AccountsController do it 'logs action' do expect(subject).to have_http_status 302 - log_item = Admin::ActionLog.last - - expect(log_item).to_not be_nil - expect(log_item.action).to eq :approve - expect(log_item.account_id).to eq current_user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:approve), + account_id: eq(current_user.account_id), + target_id: eq(account.user.id) + ) end end @@ -201,12 +202,13 @@ RSpec.describe Admin::AccountsController do it 'logs action' do expect(subject).to have_http_status 302 - log_item = Admin::ActionLog.last - - expect(log_item).to_not be_nil - expect(log_item.action).to eq :reject - expect(log_item.account_id).to eq current_user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:reject), + account_id: eq(current_user.account_id), + target_id: eq(account.user.id) + ) end end @@ -427,4 +429,10 @@ RSpec.describe Admin::AccountsController do end end end + + private + + def latest_admin_action_log + Admin::ActionLog.last + end end diff --git a/spec/controllers/admin/action_logs_controller_spec.rb b/spec/controllers/admin/action_logs_controller_spec.rb index b7854469d..be4222df0 100644 --- a/spec/controllers/admin/action_logs_controller_spec.rb +++ b/spec/controllers/admin/action_logs_controller_spec.rb @@ -9,11 +9,9 @@ describe Admin::ActionLogsController do let!(:account) { Fabricate(:account) } before do - _orphaned_logs = %w( - Account User UserRole Report DomainBlock DomainAllow - EmailDomainBlock UnavailableDomain Status AccountWarning - Announcement IpBlock Instance CustomEmoji CanonicalEmailBlock Appeal - ).map { |type| Admin::ActionLog.new(account: account, action: 'destroy', target_type: type, target_id: 1312).save! } + orphaned_log_types.map do |type| + Fabricate(:action_log, account: account, action: 'destroy', target_type: type, target_id: 1312) + end end describe 'GET #index' do @@ -24,4 +22,27 @@ describe Admin::ActionLogsController do expect(response).to have_http_status(200) end end + + private + + def orphaned_log_types + %w( + Account + AccountWarning + Announcement + Appeal + CanonicalEmailBlock + CustomEmoji + DomainAllow + DomainBlock + EmailDomainBlock + Instance + IpBlock + Report + Status + UnavailableDomain + User + UserRole + ) + end end diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index c14e08c21..4167911a1 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -21,12 +21,19 @@ RSpec.describe 'Account actions' do it 'logs action' do subject - log_item = Admin::ActionLog.last + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(action_type), + account_id: eq(user.account_id), + target_id: eq(target_type == :user ? target_account.user.id : target_account.id) + ) + end - expect(log_item).to be_present - expect(log_item.action).to eq(action_type) - expect(log_item.account_id).to eq(user.account_id) - expect(log_item.target_id).to eq(target_type == :user ? target_account.user.id : target_account.id) + private + + def latest_admin_action_log + Admin::ActionLog.last end end diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb index 8e158f623..1615581f0 100644 --- a/spec/requests/api/v1/admin/accounts_spec.rb +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -151,12 +151,13 @@ RSpec.describe 'Accounts' do it 'logs action', :aggregate_failures do subject - log_item = Admin::ActionLog.last - - expect(log_item).to be_present - expect(log_item.action).to eq :approve - expect(log_item.account_id).to eq user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:approve), + account_id: eq(user.account_id), + target_id: eq(account.user.id) + ) end end @@ -202,12 +203,13 @@ RSpec.describe 'Accounts' do it 'logs action', :aggregate_failures do subject - log_item = Admin::ActionLog.last - - expect(log_item).to be_present - expect(log_item.action).to eq :reject - expect(log_item.account_id).to eq user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:reject), + account_id: eq(user.account_id), + target_id: eq(account.user.id) + ) end end @@ -398,4 +400,10 @@ RSpec.describe 'Accounts' do end end end + + private + + def latest_admin_action_log + Admin::ActionLog.last + end end From 71e5a16ebaf67d5e55d7f4e31436c36e6801ce5a Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 10:28:19 -0500 Subject: [PATCH 13/73] Remove triple subject call in `api/v1/lists` spec (#28210) --- spec/requests/api/v1/lists_spec.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/spec/requests/api/v1/lists_spec.rb b/spec/requests/api/v1/lists_spec.rb index 22dde43a1..4635e936f 100644 --- a/spec/requests/api/v1/lists_spec.rb +++ b/spec/requests/api/v1/lists_spec.rb @@ -135,8 +135,11 @@ RSpec.describe 'Lists' do it_behaves_like 'forbidden for wrong scope', 'read read:lists' - it 'returns the updated list', :aggregate_failures do - subject + it 'returns the updated list and updates values', :aggregate_failures do + expect { subject } + .to change_list_title + .and change_list_replies_policy + .and change_list_exclusive expect(response).to have_http_status(200) list.reload @@ -149,16 +152,16 @@ RSpec.describe 'Lists' do }) end - it 'updates the list title' do - expect { subject }.to change { list.reload.title }.from('my list').to('list') + def change_list_title + change { list.reload.title }.from('my list').to('list') end - it 'updates the list replies_policy' do - expect { subject }.to change { list.reload.replies_policy }.from('list').to('followed') + def change_list_replies_policy + change { list.reload.replies_policy }.from('list').to('followed') end - it 'updates the list exclusive' do - expect { subject }.to change { list.reload.exclusive }.from(false).to(true) + def change_list_exclusive + change { list.reload.exclusive }.from(false).to(true) end context 'when the list does not exist' do From 89a8e6e6227eb901b5811d8417d81dc8ab1427a9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 4 Dec 2023 10:41:43 -0500 Subject: [PATCH 14/73] Remove 2x double subject call in `models/form/account_batch` spec (#28209) --- spec/models/form/account_batch_spec.rb | 40 +++++++++++++++++++------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/spec/models/form/account_batch_spec.rb b/spec/models/form/account_batch_spec.rb index fd8e90901..26fb1b953 100644 --- a/spec/models/form/account_batch_spec.rb +++ b/spec/models/form/account_batch_spec.rb @@ -37,12 +37,10 @@ RSpec.describe Form::AccountBatch do let(:select_all_matching) { '0' } let(:account_ids) { [target_account.id, target_account2.id] } - it 'suspends the expected users' do - expect { subject }.to change { [target_account.reload.suspended?, target_account2.reload.suspended?] }.from([false, false]).to([true, true]) - end - - it 'closes open reports targeting the suspended users' do - expect { subject }.to change { Report.unresolved.where(target_account: [target_account, target_account2]).count }.from(2).to(0) + it 'suspends the expected users and closes open reports' do + expect { subject } + .to change_account_suspensions + .and change_open_reports_for_accounts end end @@ -50,13 +48,33 @@ RSpec.describe Form::AccountBatch do let(:select_all_matching) { '1' } let(:query) { Account.where(id: [target_account.id, target_account2.id]) } - it 'suspends the expected users' do - expect { subject }.to change { [target_account.reload.suspended?, target_account2.reload.suspended?] }.from([false, false]).to([true, true]) + it 'suspends the expected users and closes open reports' do + expect { subject } + .to change_account_suspensions + .and change_open_reports_for_accounts end + end - it 'closes open reports targeting the suspended users' do - expect { subject }.to change { Report.unresolved.where(target_account: [target_account, target_account2]).count }.from(2).to(0) - end + private + + def change_account_suspensions + change { relevant_account_suspension_statuses } + .from([false, false]) + .to([true, true]) + end + + def change_open_reports_for_accounts + change(relevant_account_unresolved_reports, :count) + .from(2) + .to(0) + end + + def relevant_account_unresolved_reports + Report.unresolved.where(target_account: [target_account, target_account2]) + end + + def relevant_account_suspension_statuses + [target_account.reload, target_account2.reload].map(&:suspended?) end end end From f944a767c46e3d887e070d67958f4a9866c3d1f1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:57:16 +0100 Subject: [PATCH 15/73] Update dependency json-ld to v3.3.1 (#28229) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 037fe145d..602df6a22 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -382,15 +382,15 @@ GEM reline (>= 0.3.8) jmespath (1.6.2) json (2.7.0) - json-canonicalization (0.3.2) + json-canonicalization (1.0.0) json-jwt (1.15.3) activesupport (>= 4.2) aes_key_wrap bindata httpclient - json-ld (3.3.0) + json-ld (3.3.1) htmlentities (~> 4.3) - json-canonicalization (~> 0.3, >= 0.3.2) + json-canonicalization (~> 1.0) link_header (~> 0.0, >= 0.0.8) multi_json (~> 1.15) rack (>= 2.2, < 4) From 2d2e23c68dde6ac6fa14fe8fbeb0efac02ad9803 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:59:31 +0000 Subject: [PATCH 16/73] Update dependency brakeman to v6.1.0 (#28231) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 602df6a22..9308a41c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -175,7 +175,7 @@ GEM blurhash (0.1.7) bootsnap (1.17.0) msgpack (~> 1.2) - brakeman (6.0.1) + brakeman (6.1.0) browser (5.3.1) brpoplpush-redis_script (0.1.3) concurrent-ruby (~> 1.0, >= 1.0.5) From d0a5ebf914f7ad74a69beb15d9c011e44e84c0dc Mon Sep 17 00:00:00 2001 From: Jonathan de Jong <jonathandejong02@gmail.com> Date: Tue, 5 Dec 2023 14:59:15 +0100 Subject: [PATCH 17/73] Fix error when encountering malformed Tag objects from Kbin (#28235) --- app/services/activitypub/process_status_update_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 2db0e80e7..fb2b33114 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -170,9 +170,9 @@ class ActivityPub::ProcessStatusUpdateService < BaseService as_array(@json['tag']).each do |tag| if equals_or_includes?(tag['type'], 'Hashtag') - @raw_tags << tag['name'] + @raw_tags << tag['name'] if tag['name'].present? elsif equals_or_includes?(tag['type'], 'Mention') - @raw_mentions << tag['href'] + @raw_mentions << tag['href'] if tag['href'].present? elsif equals_or_includes?(tag['type'], 'Emoji') @raw_emojis << tag end From 4238ec844d61774a02daa4a677a69099ff494388 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:07:53 +0100 Subject: [PATCH 18/73] New Crowdin Translations (automated) (#28120) Co-authored-by: GitHub Actions <noreply@github.com> Co-authored-by: Claire <claire.github-309c@sitedethib.com> --- app/javascript/mastodon/locales/an.json | 1 + app/javascript/mastodon/locales/ca.json | 1 + app/javascript/mastodon/locales/es-MX.json | 2 +- app/javascript/mastodon/locales/fa.json | 7 +- app/javascript/mastodon/locales/fil.json | 1 + app/javascript/mastodon/locales/ko.json | 4 +- app/javascript/mastodon/locales/lt.json | 74 ++++++++++++++ app/javascript/mastodon/locales/ne.json | 1 + app/javascript/mastodon/locales/ry.json | 1 + app/javascript/mastodon/locales/sc.json | 36 +++++++ app/javascript/mastodon/locales/sk.json | 10 ++ app/javascript/mastodon/locales/sq.json | 1 + app/javascript/mastodon/locales/th.json | 14 +-- app/javascript/mastodon/locales/tlh.json | 1 + app/javascript/mastodon/locales/zh-TW.json | 2 +- config/i18n-tasks.yml | 1 + config/locales/activerecord.fil.yml | 1 + config/locales/activerecord.ne.yml | 1 + config/locales/activerecord.ry.yml | 1 + config/locales/activerecord.tlh.yml | 1 + config/locales/devise.fil.yml | 1 + config/locales/devise.ne.yml | 1 + config/locales/devise.ry.yml | 1 + config/locales/devise.tlh.yml | 1 + config/locales/doorkeeper.fil.yml | 1 + config/locales/doorkeeper.ne.yml | 1 + config/locales/doorkeeper.ry.yml | 1 + config/locales/doorkeeper.sc.yml | 15 +++ config/locales/doorkeeper.tlh.yml | 1 + config/locales/fil.yml | 1 + config/locales/fr-QC.yml | 1 + config/locales/fr.yml | 1 + config/locales/lt.yml | 26 ++++- config/locales/ne.yml | 1 + config/locales/ry.yml | 1 + config/locales/sc.yml | 113 +++++++++++++++++++++ config/locales/simple_form.fil.yml | 1 + config/locales/simple_form.lt.yml | 35 ++++++- config/locales/simple_form.ne.yml | 1 + config/locales/simple_form.ry.yml | 1 + config/locales/simple_form.sc.yml | 17 ++++ config/locales/simple_form.tlh.yml | 1 + config/locales/tlh.yml | 1 + 43 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 app/javascript/mastodon/locales/fil.json create mode 100644 app/javascript/mastodon/locales/ne.json create mode 100644 app/javascript/mastodon/locales/ry.json create mode 100644 app/javascript/mastodon/locales/tlh.json create mode 100644 config/locales/activerecord.fil.yml create mode 100644 config/locales/activerecord.ne.yml create mode 100644 config/locales/activerecord.ry.yml create mode 100644 config/locales/activerecord.tlh.yml create mode 100644 config/locales/devise.fil.yml create mode 100644 config/locales/devise.ne.yml create mode 100644 config/locales/devise.ry.yml create mode 100644 config/locales/devise.tlh.yml create mode 100644 config/locales/doorkeeper.fil.yml create mode 100644 config/locales/doorkeeper.ne.yml create mode 100644 config/locales/doorkeeper.ry.yml create mode 100644 config/locales/doorkeeper.tlh.yml create mode 100644 config/locales/fil.yml create mode 100644 config/locales/ne.yml create mode 100644 config/locales/ry.yml create mode 100644 config/locales/simple_form.fil.yml create mode 100644 config/locales/simple_form.ne.yml create mode 100644 config/locales/simple_form.ry.yml create mode 100644 config/locales/simple_form.tlh.yml create mode 100644 config/locales/tlh.yml diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json index a652272fa..b2134551b 100644 --- a/app/javascript/mastodon/locales/an.json +++ b/app/javascript/mastodon/locales/an.json @@ -499,6 +499,7 @@ "report_notification.open": "Ubrir informe", "search.placeholder": "Buscar", "search.search_or_paste": "Buscar u apegar URL", + "search_popout.full_text_search_logged_out_message": "Nomás disponible iniciando la sesión.", "search_results.all": "Totz", "search_results.hashtags": "Etiquetas", "search_results.nothing_found": "No se podió trobar cosa pa estes termins de busqueda", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 87121b7c5..5ae49325f 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -482,6 +482,7 @@ "onboarding.follows.lead": "La teva línia de temps inici només està a les teves mans. Com més gent segueixis, més activa i interessant serà. Aquests perfils poden ser un bon punt d'inici—sempre pots acabar deixant de seguir-los!:", "onboarding.follows.title": "Personalitza la pantalla d'inci", "onboarding.profile.discoverable": "Fes el meu perfil descobrible", + "onboarding.profile.discoverable_hint": "En acceptar d'ésser descobert a Mastodon els teus missatges poden aparèixer dins les tendències i els resultats de cerques, i el teu perfil es pot suggerir a qui tingui interessos semblants als teus.", "onboarding.profile.display_name": "Nom que es mostrarà", "onboarding.profile.display_name_hint": "El teu nom complet o el teu malnom…", "onboarding.profile.lead": "Sempre ho pots completar més endavant a la configuració, on hi ha encara més opcions disponibles.", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index f7cd1b330..0d26afef2 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -606,7 +606,7 @@ "search.quick_action.status_search": "Publicaciones que coinciden con {x}", "search.search_or_paste": "Buscar o pegar URL", "search_popout.full_text_search_disabled_message": "No disponible en {domain}.", - "search_popout.full_text_search_logged_out_message": "Solo disponible si inicias sesión.", + "search_popout.full_text_search_logged_out_message": "Sólo disponible al iniciar sesión.", "search_popout.language_code": "Código de idioma ISO", "search_popout.options": "Opciones de búsqueda", "search_popout.quick_actions": "Acciones rápidas", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 6951d5cb0..8e8930bfe 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -1,7 +1,7 @@ { "about.blocks": "کارسازهای نظارت شده", "about.contact": "تماس:", - "about.disclaimer": "ماستودون نرمافزار آزاد و یک شرکت غیر انتفاعی آلمانی با مسئولیت محدود است.", + "about.disclaimer": "ماستودون نرمافزار آزاد و نشان تجاری یک شرکت غیر انتفاعی با مسئولیت محدود آلمانی است.", "about.domain_blocks.no_reason_available": "دلیلی موجود نیست", "about.domain_blocks.preamble": "ماستودون عموماً میگذارد محتوا را از از هر کارساز دیگری در دنیای شبکههای اجتماعی غیرمتمرکز دیده و با آنان برهمکنش داشته باشید. اینها استثناهایی هستند که روی این کارساز خاص وضع شدهاند.", "about.domain_blocks.silenced.explanation": "عموماً نمایهها و محتوا از این کارساز را نمیبینید، مگر این که به طور خاص دنبالشان گشته یا با پی گیری، داوطلب دیدنشان شوید.", @@ -21,6 +21,7 @@ "account.blocked": "مسدود", "account.browse_more_on_origin_server": "مرور بیشتر روی نمایهٔ اصلی", "account.cancel_follow_request": "رد کردن درخواست پیگیری", + "account.copy": "رونوشت از پیوند به نمایه", "account.direct": "اشارهٔ خصوصی به @{name}", "account.disable_notifications": "آگاه کردن من هنگام فرستههای @{name} را متوقّف کن", "account.domain_blocked": "دامنه مسدود شد", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "علامتگذاری به عنوان خوانده شده", "conversation.open": "دیدن گفتگو", "conversation.with": "با {names}", + "copy_icon_button.copied": "در بریدهدان رونوشت شد", "copypaste.copied": "رونوشت شد", "copypaste.copy_to_clipboard": "رونوشت به تختهگیره", "directory.federated": "از کارسازهای شناختهشده", @@ -486,6 +488,8 @@ "onboarding.profile.note_hint": "میتوانید افراد دیگر را @نامبردن یا #برچسب بزنید…", "onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده", "onboarding.profile.title": "تنظیم نمایه", + "onboarding.profile.upload_avatar": "بازگذاری تصویر نمایه", + "onboarding.profile.upload_header": "بارگذاری تصویر سردر نمایه", "onboarding.share.lead": "بگذارید افراد بدانند چگونه میتوانند در ماستادون بیابندتان!", "onboarding.share.message": "من {username} روی #ماستودون هستم! مرا در {url} پیبگیرید", "onboarding.share.next_steps": "گامهای ممکن بعدی:", @@ -600,6 +604,7 @@ "search.quick_action.status_search": "فرستههای جور با {x}", "search.search_or_paste": "جستوجو یا جایگذاری نشانی", "search_popout.full_text_search_disabled_message": "روی {domain} موجود نیست.", + "search_popout.full_text_search_logged_out_message": "تنها زمانی که وارد شدهاید دردسترس است.", "search_popout.language_code": "کد زبان ایزو", "search_popout.options": "گزینههای جستوجو", "search_popout.quick_actions": "کنشهای سریع", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/fil.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 46caf32b0..5b76cd67c 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -302,8 +302,8 @@ "hashtag.counter_by_accounts": "{count, plural, other {{counter} 명의 참여자}}", "hashtag.counter_by_uses": "{count, plural, other {{counter} 개의 게시물}}", "hashtag.counter_by_uses_today": "오늘 {count, plural, other {{counter} 개의 게시물}}", - "hashtag.follow": "해시태그 팔로우", - "hashtag.unfollow": "해시태그 팔로우 해제", + "hashtag.follow": "팔로우", + "hashtag.unfollow": "팔로우 해제", "hashtags.and_other": "…그리고 {count, plural,other {#개 더}}", "home.actions.go_to_explore": "무엇이 유행인지 보기", "home.actions.go_to_suggestions": "팔로우할 사람 찾기", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index a976d5907..e588b8538 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -180,29 +180,71 @@ "confirmations.logout.message": "Ar tikrai nori atsijungti?", "confirmations.mute.confirm": "Nutildyti", "confirmations.mute.explanation": "Tai paslėps jų įrašus ir įrašus, kuriuose jie menėmi, tačiau jie vis tiek galės matyti tavo įrašus ir sekti.", + "confirmations.mute.message": "Ar tikrai norite nutildyti {name}?", + "confirmations.redraft.confirm": "Ištrinti ir perrašyti", "confirmations.reply.confirm": "Atsakyti", "confirmations.reply.message": "Atsakant dabar, bus perrašyta metu kuriama žinutė. Ar tikrai nori tęsti?", "confirmations.unfollow.confirm": "Nebesekti", + "confirmations.unfollow.message": "Ar tikrai norite atsisakyti sekimo {name}?", + "conversation.delete": "Ištrinti pokalbį", "conversation.mark_as_read": "Žymėti kaip skaitytą", "conversation.open": "Peržiūrėti pokalbį", "conversation.with": "Su {names}", "copy_icon_button.copied": "Nukopijuota į iškarpinę", "copypaste.copied": "Nukopijuota", "copypaste.copy_to_clipboard": "Kopijuoti į iškarpinę", + "directory.local": "Iš {domain} tik", + "directory.new_arrivals": "Naujos prekės", + "directory.recently_active": "Neseniai aktyvus", "disabled_account_banner.account_settings": "Paskyros nustatymai", + "disabled_account_banner.text": "Jūsų paskyra {disabledAccount} šiuo metu yra išjungta.", + "dismissable_banner.dismiss": "Atmesti", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", + "dismissable_banner.explore_statuses": "Tai įrašai iš viso socialinio tinklo, kurie šiandien sulaukia vis daugiau dėmesio. Naujesni įrašai, turintys daugiau boosts ir mėgstamiausių įrašų, yra vertinami aukščiau.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Štai kaip tai atrodys:", + "emoji_button.activity": "Veikla", + "emoji_button.clear": "Išvalyti", + "emoji_button.custom": "Pasirinktinis", + "emoji_button.flags": "Vėliavos", + "emoji_button.food": "Maistas ir Gėrimai", + "emoji_button.label": "Įterpti veidelius", + "emoji_button.nature": "Gamta", + "emoji_button.not_found": "Nerasta jokių tinkamų jaustukų", "emoji_button.objects": "Objektai", + "emoji_button.people": "Žmonės", + "emoji_button.recent": "Dažniausiai naudojama", "emoji_button.search": "Paieška...", + "emoji_button.search_results": "Paieškos rezultatai", + "emoji_button.symbols": "Simboliai", + "emoji_button.travel": "Kelionės ir Vietos", "empty_column.account_hides_collections": "Šis naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą", + "empty_column.account_suspended": "Paskyra sustabdyta", "empty_column.account_timeline": "No toots here!", + "empty_column.account_unavailable": "Profilis neprieinamas", + "empty_column.blocks": "Dar neužblokavote nė vieno naudotojo.", "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.", + "empty_column.community": "Vietinė laiko juosta yra tuščia. Parašykite ką nors viešai, kad pradėtumėte veikti!", + "empty_column.direct": "Dar neturite jokių privačių paminėjimų. Kai išsiųsite arba gausite tokį pranešimą, jis bus rodomas čia.", "empty_column.domain_blocks": "There are no hidden domains yet.", + "empty_column.favourited_statuses": "Dar neturite mėgstamiausių įrašų. Kai vieną iš jų pamėgsite, jis bus rodomas čia.", + "empty_column.follow_requests": "Dar neturite jokių sekimo užklausų. Kai gausite tokį prašymą, jis bus rodomas čia.", + "empty_column.followed_tags": "Dar nesekėte jokių grotažymių. Kai tai padarysite, jie bus rodomi čia.", "empty_column.hashtag": "Nėra nieko šiame saitažodyje kol kas.", "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.lists": "Dar neturite jokių sąrašų. Kai jį sukursite, jis bus rodomas čia.", + "empty_column.mutes": "Dar nesate nutildę nė vieno naudotojo.", + "empty_column.notifications": "Dar neturite jokių pranešimų. Kai kiti žmonės su jumis bendraus, matysite tai čia.", + "empty_column.public": "Čia nieko nėra! Parašykite ką nors viešai arba rankiniu būdu sekite naudotojus iš kitų serverių, kad jį užpildytumėte", + "error.unexpected_crash.explanation": "Dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos šis puslapis negalėjo būti rodomas teisingai.", + "error.unexpected_crash.explanation_addons": "Šį puslapį nepavyko teisingai parodyti. Šią klaidą greičiausiai sukėlė naršyklės priedas arba automatinio vertimo įrankiai.", + "error.unexpected_crash.next_steps": "Pabandykite atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsite naudotis \"Mastodon\" naudodami kitą naršyklę arba vietinę programėlę.", + "error.unexpected_crash.next_steps_addons": "Pabandykite juos išjungti ir atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsite naudotis \"Mastodon\" naudodami kitą naršyklę arba vietinę programėlę.", + "errors.unexpected_crash.report_issue": "Pranešti apie triktį", + "explore.search_results": "Paieškos rezultatai", + "explore.suggested_follows": "Žmonės", "explore.title": "Naršyti", "explore.trending_links": "Naujienos", "explore.trending_statuses": "Įrašai", @@ -304,7 +346,13 @@ "moved_to_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu yra išjungta, nes persikėlei į {movedToAccount}.", "mute_modal.duration": "Trukmė", "mute_modal.hide_notifications": "Slėpti šio naudotojo pranešimus?", + "mute_modal.indefinite": "Neribotas", + "navigation_bar.about": "Apie", + "navigation_bar.advanced_interface": "Atidarykite išplėstinę žiniatinklio sąsają", + "navigation_bar.blocks": "Užblokuoti naudotojai", + "navigation_bar.bookmarks": "Žymės", "navigation_bar.compose": "Compose new toot", + "navigation_bar.direct": "Privatūs paminėjimai", "navigation_bar.discover": "Atrasti", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Redaguoti profilį", @@ -372,6 +420,7 @@ "notifications.permission_required": "Darbalaukio pranešimai nepasiekiami, nes nesuteiktas reikiamas leidimas.", "notifications_permission_banner.enable": "Įjungti darbalaukio pranešimus", "notifications_permission_banner.how_to_control": "Jei norite gauti pranešimus, kai \"Mastodon\" nėra atidarytas, įjunkite darbalaukio pranešimus. Įjungę darbalaukio pranešimus, galite tiksliai valdyti, kokių tipų sąveikos generuoja darbalaukio pranešimus, naudodamiesi pirmiau esančiu mygtuku {icon}.", + "notifications_permission_banner.title": "Niekada nieko nepraleiskite", "onboarding.action.back": "Gražinkite mane atgal", "onboarding.actions.back": "Gražinkite mane atgal", "onboarding.actions.go_to_explore": "See what's trending", @@ -394,8 +443,10 @@ "onboarding.share.lead": "Praneškite žmonėms, kaip jus rasti \"Mastodon\"!", "onboarding.share.message": "Aš {username} #Mastodon! Ateik sekti manęs adresu {url}", "onboarding.share.next_steps": "Galimi kiti žingsniai:", + "onboarding.share.title": "Bendrinkite savo profilį", "onboarding.start.lead": "Dabar esi Mastodon dalis – unikalios decentralizuotos socialinės žiniasklaidos platformos, kurioje tu, o ne algoritmas, pats nustatai savo patirtį. Pradėkime tavo kelionę šioje naujoje socialinėje erdvėje:", "onboarding.start.skip": "Want to skip right ahead?", + "onboarding.start.title": "Jums pavyko!", "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}", "onboarding.steps.publish_status.body": "Say hello to the world.", @@ -404,23 +455,46 @@ "onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.title": "Share your profile", + "picture_in_picture.restore": "Padėkite jį atgal", + "poll.closed": "Uždaryti", + "poll.refresh": "Atnaujinti", + "poll.reveal": "Peržiūrėti rezultatus", "poll.vote": "Balsuoti", "poll.voted": "Tu balsavai už šį atsakymą", "poll.votes": "{votes, plural, one {# balsas} few {# balsai} many {# balso} other {# balsų}}", + "poll_button.add_poll": "Pridėti apklausą", + "poll_button.remove_poll": "Šalinti apklausą", "privacy.change": "Adjust status privacy", "privacy.direct.long": "Post to mentioned users only", "privacy.direct.short": "Direct", "privacy.private.long": "Post to followers only", "privacy.private.short": "Followers-only", + "privacy.public.long": "Visiems matomas", + "privacy.public.short": "Viešas", "privacy.unlisted.long": "Matomas visiems, bet atsisakyta atradimo funkcijų", "privacy.unlisted.short": "Neįtrauktas į sąrašą", "privacy_policy.last_updated": "Paskutinį kartą atnaujinta {date}", + "privacy_policy.title": "Privatumo politika", "recommended": "Rekomenduojama", + "refresh": "Atnaujinti", + "regeneration_indicator.label": "Kraunasi…", + "relative_time.full.just_now": "ką tik", "relative_time.hours": "{number} val.", "relative_time.just_now": "dabar", "relative_time.minutes": "{number} min.", "relative_time.seconds": "{number} sek.", "relative_time.today": "šiandien", + "reply_indicator.cancel": "Atšaukti", + "report.block": "Blokuoti", + "report.categories.legal": "Legalus", + "report.categories.other": "Kita", + "report.categories.spam": "Šlamštas", + "report.categories.violation": "Turinys pažeidžia vieną ar daugiau serverio taisyklių", + "report.category.subtitle": "Pasirinkite tinkamiausią variantą", + "report.category.title_account": "profilis", + "report.category.title_status": "įrašas", + "report.close": "Atlikta", + "report.comment.title": "Ar yra dar kas nors, ką, jūsų manymu, turėtume žinoti?", "report.mute_explanation": "Jų įrašų nematysi. Jie vis tiek gali tave sekti ir matyti įrašus, bet nežinos, kad jie nutildyti.", "report.next": "Tęsti", "report.placeholder": "Papildomi komentarai", diff --git a/app/javascript/mastodon/locales/ne.json b/app/javascript/mastodon/locales/ne.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/ne.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/ry.json b/app/javascript/mastodon/locales/ry.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/ry.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 59c834b95..7f29525e7 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -14,6 +14,7 @@ "account.badges.group": "Grupu", "account.block": "Bloca @{name}", "account.block_domain": "Bloca su domìniu {domain}", + "account.block_short": "Bloca", "account.blocked": "Blocadu", "account.browse_more_on_origin_server": "Esplora de prus in su profilu originale", "account.cancel_follow_request": "Withdraw follow request", @@ -31,17 +32,20 @@ "account.follows.empty": "Custa persone non sighit ancora a nemos.", "account.follows_you": "Ti sighit", "account.hide_reblogs": "Cua is cumpartziduras de @{name}", + "account.in_memoriam": "In memoriam.", "account.joined_short": "At aderidu", "account.link_verified_on": "Sa propiedade de custu ligòngiu est istada controllada su {date}", "account.locked_info": "S'istadu de riservadesa de custu contu est istadu cunfiguradu comente blocadu. Sa persone chi tenet sa propiedade revisionat a manu chie dda podet sighire.", "account.media": "Cuntenutu multimediale", "account.mention": "Mèntova a @{name}", "account.mute": "Pone a @{name} a sa muda", + "account.mute_short": "A sa muda", "account.muted": "A sa muda", "account.posts": "Publicatziones", "account.posts_with_replies": "Publicatziones e rispostas", "account.report": "Signala @{name}", "account.requested": "Abetende s'aprovatzione. Incarca pro annullare sa rechesta de sighidura", + "account.requested_follow": "{name} at dimandadu de ti sighire", "account.share": "Cumpartzi su profilu de @{name}", "account.show_reblogs": "Ammustra is cumpartziduras de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicatzione} other {{counter} publicatziones}}", @@ -106,6 +110,7 @@ "compose_form.publish": "Pùblica", "compose_form.publish_form": "Publish", "compose_form.publish_loud": "{publish}!", + "compose_form.save_changes": "Sarva is modìficas", "compose_form.sensitive.hide": "{count, plural, one {Marca elementu multimediale comente a sensìbile} other {Marca elementos multimediales comente sensìbiles}}", "compose_form.sensitive.marked": "{count, plural, one {Elementu multimediale marcadu comente a sensìbile} other {Elementos multimediales marcados comente a sensìbiles}}", "compose_form.sensitive.unmarked": "{count, plural, one {Elementu multimediale non marcadu comente a sensìbile} other {Elementos multimediales non marcados comente a sensìbiles}}", @@ -122,6 +127,7 @@ "confirmations.delete_list.message": "Seguru chi boles cantzellare custa lista in manera permanente?", "confirmations.domain_block.confirm": "Bloca totu su domìniu", "confirmations.domain_block.message": "Boles de seguru, ma a beru a beru, blocare {domain}? In sa parte manna de is casos, pagos blocos o silentziamentos de persones sunt sufitzientes e preferìbiles. No as a bìdere cuntenutos dae custu domìniu in peruna lìnia de tempus pùblica o in is notìficas tuas. Sa gente chi ti sighit dae cussu domìniu at a èssere bogada.", + "confirmations.edit.confirm": "Modìfica", "confirmations.logout.confirm": "Essi·nche", "confirmations.logout.message": "Seguru chi boles essire?", "confirmations.mute.confirm": "A sa muda", @@ -140,6 +146,7 @@ "directory.local": "Isceti dae {domain}", "directory.new_arrivals": "Arribos noos", "directory.recently_active": "Cun atividade dae pagu", + "disabled_account_banner.account_settings": "Cunfiguratziones de su contu", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "embed.instructions": "Inserta custa publicatzione in su situ web tuo copiende su còdighe de suta.", @@ -180,13 +187,19 @@ "errors.unexpected_crash.copy_stacktrace": "Còpia stacktrace in punta de billete", "errors.unexpected_crash.report_issue": "Sinnala unu problema", "explore.search_results": "Resurtados de sa chirca", + "explore.suggested_follows": "Gente", + "explore.trending_statuses": "Publicatziones", + "explore.trending_tags": "Etichetas", "filter_modal.select_filter.expired": "iscadidu", + "firehose.all": "Totus", "follow_request.authorize": "Autoriza", "follow_request.reject": "Refuda", "follow_requests.unlocked_explanation": "Fintzas si su contu tuo no est blocadu, su personale de {domain} at pensadu chi forsis bolias revisionare a manu is rechestas de custos contos.", "footer.about": "Informatziones", "footer.invite": "Invita gente", + "footer.keyboard_shortcuts": "Incurtzaduras de tecladu", "footer.privacy_policy": "Polìtica de riservadesa", + "footer.status": "Istadu", "generic.saved": "Sarvadu", "getting_started.heading": "Comente cumintzare", "hashtag.column_header.tag_mode.all": "e {additional}", @@ -263,6 +276,7 @@ "lists.search": "Chirca intre sa gente chi ses sighende", "lists.subheading": "Is listas tuas", "load_pending": "{count, plural, one {# elementu nou} other {# elementos noos}}", + "loading_indicator.label": "Carrighende…", "media_gallery.toggle_visible": "Cua {number, plural, one {immàgine} other {immàgines}}", "mute_modal.duration": "Durada", "mute_modal.hide_notifications": "Boles cuare is notìficas de custa persone?", @@ -288,6 +302,7 @@ "navigation_bar.search": "Chirca", "navigation_bar.security": "Seguresa", "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.", + "notification.favourite": "{name} at marcadu comente a preferidu s'istadu tuo", "notification.follow": "{name} ti sighit", "notification.follow_request": "{name} at dimandadu de ti sighire", "notification.mention": "{name} t'at mentovadu", @@ -328,6 +343,8 @@ "onboarding.actions.go_to_home": "Go to your home feed", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.display_name": "Nòmine visìbile", + "onboarding.profile.note": "Biografia", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.skip": "Want to skip right ahead?", "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", @@ -344,6 +361,7 @@ "poll.total_votes": "{count, plural, one {# votu} other {# votos}}", "poll.vote": "Vota", "poll.voted": "As votadu custa risposta", + "poll.votes": "{votes, plural, one {# votu} other {# votos}}", "poll_button.add_poll": "Agiunghe unu sondàgiu", "poll_button.remove_poll": "Cantzella su sondàgiu", "privacy.change": "Modìfica s'istadu de riservadesa", @@ -353,25 +371,41 @@ "privacy.private.short": "Followers-only", "privacy.public.short": "Pùblicu", "privacy.unlisted.short": "Esclùidu de sa lista", + "recommended": "Cussigiadu", "refresh": "Atualiza", "regeneration_indicator.label": "Carrighende…", "regeneration_indicator.sublabel": "Preparende sa lìnia de tempus printzipale tua.", "relative_time.days": "{number} dies a oe", + "relative_time.full.just_now": "immoe etotu", "relative_time.hours": "{number} oras a immoe", "relative_time.just_now": "immoe", "relative_time.minutes": "{number} minutos a immoe", "relative_time.seconds": "{number} segundos a immoe", "relative_time.today": "oe", "reply_indicator.cancel": "Annulla", + "report.block": "Bloca", + "report.categories.other": "Àteru", + "report.category.title_account": "profilu", + "report.category.title_status": "publicatzione", + "report.close": "Fatu", "report.forward": "Torra a imbiare a {target}", "report.forward_hint": "Custu contu est de un'àteru serbidore. Ddi boles imbiare puru una còpia anònima de custu informe?", + "report.mute": "A sa muda", + "report.next": "Imbeniente", "report.placeholder": "Cummentos additzionales", "report.submit": "Imbia", "report.target": "Informende de {target}", "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "report_notification.categories.other": "Àteru", "search.placeholder": "Chirca", + "search_popout.user": "utente", + "search_results.accounts": "Profilos", + "search_results.all": "Totus", "search_results.hashtags": "Etichetas", "search_results.statuses": "Publicatziones", + "server_banner.administered_by": "Amministradu dae:", + "server_banner.learn_more": "Àteras informatziones", + "server_banner.server_stats": "Istatìsticas de su serbidore:", "sign_in_banner.sign_in": "Sign in", "status.admin_account": "Aberi s'interfache de moderatzione pro @{name}", "status.admin_status": "Aberi custa publicatzione in s'interfache de moderatzione", @@ -382,6 +416,7 @@ "status.copy": "Còpia su ligòngiu a sa publicatzione tua", "status.delete": "Cantzella", "status.detailed_status": "Visualizatzione de detàlliu de arresonada", + "status.edit": "Modìfica", "status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}", "status.embed": "Afissa", "status.filtered": "Filtradu", @@ -413,6 +448,7 @@ "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", "status.unmute_conversation": "Torra a ativare s'arresonada", "status.unpin": "Boga dae pitzu de su profilu", + "subscribed_languages.save": "Sarva is modìficas", "tabs_bar.home": "Printzipale", "tabs_bar.notifications": "Notìficas", "time_remaining.days": "{number, plural, one {abarrat # die} other {abarrant # dies}}", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 9115695d8..c4ce6f8cf 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -391,6 +391,7 @@ "lists.search": "Vyhľadávaj medzi užívateľmi, ktorých sleduješ", "lists.subheading": "Tvoje zoznamy", "load_pending": "{count, plural, one {# nová položka} other {# nových položiek}}", + "loading_indicator.label": "Načítam…", "media_gallery.toggle_visible": "Zapni/Vypni viditeľnosť", "moved_to_account_banner.text": "Vaše konto {disabledAccount} je momentálne zablokované, pretože ste sa presunuli na {movedToAccount}.", "mute_modal.duration": "Trvanie", @@ -480,6 +481,14 @@ "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", "onboarding.profile.discoverable": "Urob môj profil objaviteľný", + "onboarding.profile.display_name": "Zobrazované meno", + "onboarding.profile.display_name_hint": "Tvoje plné meno, alebo tvoje zábavné meno…", + "onboarding.profile.lead": "Toto môžeš vždy dokončiť neskôr v nastaveniach, kde je dostupných ešte viac volieb na prispôsobenie.", + "onboarding.profile.note": "O tebe", + "onboarding.profile.note_hint": "Môžeš @spomenúť iných ľudí, alebo #haštagy…", + "onboarding.profile.save_and_continue": "Ulož a pokračuj", + "onboarding.profile.upload_avatar": "Nahraj profilový obrázok", + "onboarding.profile.upload_header": "Nahraj profilové záhlavie", "onboarding.share.lead": "Daj ľudom vedieť, ako ťa môžu na Mastodone nájsť!", "onboarding.share.message": "Na Mastodone som {username}. Príď ma nasledovať na {url}", "onboarding.share.next_steps": "Ďalšie možné kroky:", @@ -594,6 +603,7 @@ "search.quick_action.status_search": "Príspevky zodpovedajúce {x}", "search.search_or_paste": "Hľadaj, alebo vlož URL adresu", "search_popout.full_text_search_disabled_message": "Nie je k dispozícii v doméne {domain}.", + "search_popout.full_text_search_logged_out_message": "Dostupné iba keď si prihlásený/á.", "search_popout.language_code": "ISO kód jazyka", "search_popout.options": "Možnosti vyhľadávania", "search_popout.quick_actions": "Rýchle akcie", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 86042a91e..710224e1c 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -606,6 +606,7 @@ "search.quick_action.status_search": "Postime me përputhje me {x}", "search.search_or_paste": "Kërkoni, ose hidhni një URL", "search_popout.full_text_search_disabled_message": "Jo i passhëm në {domain}.", + "search_popout.full_text_search_logged_out_message": "E përdorshme vetëm kur keni bërë hyrjen në llogari.", "search_popout.language_code": "Kod ISO gjuhe", "search_popout.options": "Mundësi kërkimi", "search_popout.quick_actions": "Veprime të shpejta", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index c020dc362..0be9bf6d7 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -60,7 +60,7 @@ "account.report": "รายงาน @{name}", "account.requested": "กำลังรอการอนุมัติ คลิกเพื่อยกเลิกคำขอติดตาม", "account.requested_follow": "{name} ได้ขอติดตามคุณ", - "account.share": "แบ่งปันโปรไฟล์ของ @{name}", + "account.share": "แชร์โปรไฟล์ของ @{name}", "account.show_reblogs": "แสดงการดันจาก @{name}", "account.statuses_counter": "{count, plural, other {{counter} โพสต์}}", "account.unblock": "เลิกปิดกั้น @{name}", @@ -319,7 +319,7 @@ "home.show_announcements": "แสดงประกาศ", "interaction_modal.description.favourite": "ด้วยบัญชีใน Mastodon คุณสามารถชื่นชอบโพสต์นี้เพื่อแจ้งให้ผู้สร้างทราบว่าคุณชื่นชมโพสต์และบันทึกโพสต์ไว้สำหรับภายหลัง", "interaction_modal.description.follow": "ด้วยบัญชีใน Mastodon คุณสามารถติดตาม {name} เพื่อรับโพสต์ของเขาในฟีดหน้าแรกของคุณ", - "interaction_modal.description.reblog": "ด้วยบัญชีใน Mastodon คุณสามารถดันโพสต์นี้เพื่อแบ่งปันโพสต์กับผู้ติดตามของคุณเอง", + "interaction_modal.description.reblog": "ด้วยบัญชีใน Mastodon คุณสามารถดันโพสต์นี้เพื่อแชร์โพสต์กับผู้ติดตามของคุณเอง", "interaction_modal.description.reply": "ด้วยบัญชีใน Mastodon คุณสามารถตอบกลับโพสต์นี้", "interaction_modal.login.action": "นำฉันกลับบ้าน", "interaction_modal.login.prompt": "โดเมนของเซิร์ฟเวอร์บ้านของคุณ เช่น mastodon.social", @@ -495,7 +495,7 @@ "onboarding.share.lead": "แจ้งให้ผู้คนทราบวิธีที่เขาสามารถค้นหาคุณใน Mastodon!", "onboarding.share.message": "ฉันคือ {username} ใน #Mastodon! มาติดตามฉันที่ {url}", "onboarding.share.next_steps": "ขั้นตอนถัดไปที่เป็นไปได้:", - "onboarding.share.title": "แบ่งปันโปรไฟล์ของคุณ", + "onboarding.share.title": "แชร์โปรไฟล์ของคุณ", "onboarding.start.lead": "ตอนนี้คุณเป็นส่วนหนึ่งของ Mastodon แพลตฟอร์มสื่อสังคมที่มีเอกลักษณ์เฉพาะตัว กระจายศูนย์ ที่ซึ่งคุณ—ไม่ใช่อัลกอริทึม—เรียบเรียงประสบการณ์ของคุณเอง มาช่วยให้คุณเริ่มต้นใช้งานพรมแดนทางสังคมใหม่นี้กันเลย:", "onboarding.start.skip": "ไม่ต้องการความช่วยเหลือในการเริ่มต้นใช้งาน?", "onboarding.start.title": "คุณทำสำเร็จแล้ว!", @@ -506,7 +506,7 @@ "onboarding.steps.setup_profile.body": "เพิ่มการโต้ตอบของคุณโดยการมีโปรไฟล์ที่ครอบคลุม", "onboarding.steps.setup_profile.title": "ปรับแต่งโปรไฟล์ของคุณ", "onboarding.steps.share_profile.body": "แจ้งให้เพื่อน ๆ ของคุณทราบวิธีค้นหาคุณใน Mastodon", - "onboarding.steps.share_profile.title": "แบ่งปันโปรไฟล์ Mastodon ของคุณ", + "onboarding.steps.share_profile.title": "แชร์โปรไฟล์ Mastodon ของคุณ", "onboarding.tips.2fa": "<strong>คุณทราบหรือไม่?</strong> คุณสามารถรักษาความปลอดภัยบัญชีของคุณได้โดยตั้งค่าการรับรองความถูกต้องด้วยสองปัจจัยในการตั้งค่าบัญชีของคุณ การรับรองความถูกต้องด้วยสองปัจจัยทำงานร่วมกับแอป TOTP ใด ๆ ที่คุณเลือก ไม่จำเป็นต้องมีหมายเลขโทรศัพท์!", "onboarding.tips.accounts_from_other_servers": "<strong>คุณทราบหรือไม่?</strong> เนื่องจาก Mastodon เป็นแบบกระจายศูนย์ โปรไฟล์บางส่วนที่คุณเจอจะได้รับการโฮสต์ในเซิร์ฟเวอร์อื่น ๆ ที่ไม่ใช่ของคุณ และคุณยังสามารถโต้ตอบกับเขาได้อย่างไร้รอยต่อ! เซิร์ฟเวอร์ของเขาอยู่ในครึ่งหลังของชื่อผู้ใช้ของเขา!", "onboarding.tips.migration": "<strong>คุณทราบหรือไม่?</strong> หากคุณรู้สึกว่า {domain} ไม่ใช่ตัวเลือกเซิร์ฟเวอร์ที่ยอดเยี่ยมสำหรับคุณในอนาคต คุณสามารถย้ายไปยังเซิร์ฟเวอร์ Mastodon อื่นได้โดยไม่สูญเสียผู้ติดตามของคุณ คุณยังสามารถโฮสต์เซิร์ฟเวอร์ของคุณเอง!", @@ -558,7 +558,7 @@ "report.categories.spam": "สแปม", "report.categories.violation": "เนื้อหาละเมิดกฎของเซิร์ฟเวอร์จำนวนหนึ่งหรือมากกว่า", "report.category.subtitle": "เลือกที่ตรงกันที่สุด", - "report.category.title": "บอกเราถึงสิ่งที่กำลังเกิดขึ้นกับ {type} นี้", + "report.category.title": "บอกเราถึงสิ่งที่กำลังเกิดขึ้นกับ{type}นี้", "report.category.title_account": "โปรไฟล์", "report.category.title_status": "โพสต์", "report.close": "เสร็จสิ้น", @@ -629,7 +629,7 @@ "sign_in_banner.create_account": "สร้างบัญชี", "sign_in_banner.sign_in": "เข้าสู่ระบบ", "sign_in_banner.sso_redirect": "เข้าสู่ระบบหรือลงทะเบียน", - "sign_in_banner.text": "เข้าสู่ระบบเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แบ่งปัน และตอบกลับโพสต์ คุณยังสามารถโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น", + "sign_in_banner.text": "เข้าสู่ระบบเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แชร์ และตอบกลับโพสต์ คุณยังสามารถโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น", "status.admin_account": "เปิดส่วนติดต่อการควบคุมสำหรับ @{name}", "status.admin_domain": "เปิดส่วนติดต่อการควบคุมสำหรับ {domain}", "status.admin_status": "เปิดโพสต์นี้ในส่วนติดต่อการควบคุม", @@ -675,7 +675,7 @@ "status.replyAll": "ตอบกลับกระทู้", "status.report": "รายงาน @{name}", "status.sensitive_warning": "เนื้อหาที่ละเอียดอ่อน", - "status.share": "แบ่งปัน", + "status.share": "แชร์", "status.show_filter_reason": "แสดงต่อไป", "status.show_less": "แสดงน้อยลง", "status.show_less_all": "แสดงน้อยลงทั้งหมด", diff --git a/app/javascript/mastodon/locales/tlh.json b/app/javascript/mastodon/locales/tlh.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/tlh.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 7ba66ae8c..e6dd008bf 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -376,7 +376,7 @@ "lightbox.previous": "上一步", "limited_account_hint.action": "一律顯示個人檔案", "limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。", - "link_preview.author": "由 {name} 提供", + "link_preview.author": "來自 {name}", "lists.account.add": "新增至列表", "lists.account.remove": "自列表中移除", "lists.delete": "刪除列表", diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index e2ea4153c..8463d4297 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -70,6 +70,7 @@ ignore_unused: - 'imports.preambles.{following,blocking,muting,domain_blocking,bookmarks,lists}_html' - 'mail_subscriptions.unsubscribe.emails.*' - 'preferences.other' # some locales are missing other keys, therefore leading i18n-tasks to detect `preferences` as plural and not finding use + - 'edit_profile.other' # some locales are missing other keys, therefore leading i18n-tasks to detect `preferences` as plural and not finding use ignore_inconsistent_interpolations: - '*.one' diff --git a/config/locales/activerecord.fil.yml b/config/locales/activerecord.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/activerecord.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/activerecord.ne.yml b/config/locales/activerecord.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/activerecord.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/activerecord.ry.yml b/config/locales/activerecord.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/activerecord.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/activerecord.tlh.yml b/config/locales/activerecord.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/activerecord.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/devise.fil.yml b/config/locales/devise.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/devise.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/devise.ne.yml b/config/locales/devise.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/devise.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/devise.ry.yml b/config/locales/devise.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/devise.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/devise.tlh.yml b/config/locales/devise.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/devise.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/doorkeeper.fil.yml b/config/locales/doorkeeper.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/doorkeeper.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/doorkeeper.ne.yml b/config/locales/doorkeeper.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/doorkeeper.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/doorkeeper.ry.yml b/config/locales/doorkeeper.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/doorkeeper.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/doorkeeper.sc.yml b/config/locales/doorkeeper.sc.yml index 1f1d38f3a..297d6bd8f 100644 --- a/config/locales/doorkeeper.sc.yml +++ b/config/locales/doorkeeper.sc.yml @@ -69,6 +69,7 @@ sc: confirmations: revoke: Seguru? index: + scopes: Permissos title: Is aplicatziones autorizadas tuas errors: messages: @@ -104,6 +105,20 @@ sc: authorized_applications: destroy: notice: Aplicatzione revocada. + grouped_scopes: + title: + accounts: Contos + bookmarks: Sinnalibros + conversations: Arresonadas + filters: Filtros + follows: Sighende + lists: Listas + media: Allegados multimediales + notifications: Notìficas + push: Notìficas push + reports: Informes + search: Chirca + statuses: Publicatziones layouts: admin: nav: diff --git a/config/locales/doorkeeper.tlh.yml b/config/locales/doorkeeper.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/doorkeeper.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/fil.yml b/config/locales/fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/fr-QC.yml b/config/locales/fr-QC.yml index 3aba8713f..00a59463c 100644 --- a/config/locales/fr-QC.yml +++ b/config/locales/fr-QC.yml @@ -611,6 +611,7 @@ fr-QC: created_at: Signalé delete_and_resolve: Supprimer les messages forwarded: Transféré + forwarded_replies_explanation: Ce rapport provient d'un utilisateur sur une autre instance et concerne du contenu non-local. Il vous a été transmis car le contenu signalé est en réponse à l'un de vos utilisateurs. forwarded_to: Transféré à %{domain} mark_as_resolved: Marquer comme résolu mark_as_sensitive: Marquer comme sensible diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a69a5b535..0a6601bbc 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -611,6 +611,7 @@ fr: created_at: Signalé delete_and_resolve: Supprimer les messages forwarded: Transféré + forwarded_replies_explanation: Ce rapport provient d'un utilisateur sur une autre instance et concerne du contenu non-local. Il vous a été transmis car le contenu signalé est en réponse à l'un de vos utilisateurs. forwarded_to: Transféré à %{domain} mark_as_resolved: Marquer comme résolu mark_as_sensitive: Marquer comme sensible diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 111127749..b1d8772b6 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -37,10 +37,12 @@ lt: accounts: add_email_domain_block: Blokuoti el. pašto domeną approve: Patvirtinti + approved_msg: Sėkmingai patvirtinta %{username} registracijos paraiška are_you_sure: Ar esi įsitikinęs (-usi)? avatar: Avataras by_domain: Domenas change_email: + changed_msg: El. paštas sėkmingai pakeistas! current_email: Dabartinis el paštas label: Pakeisti el pašto adresą new_email: Naujas el pašto adresas @@ -466,6 +468,8 @@ lt: prev: Ankstesnis preferences: other: Kita + privacy: + hint_html: "<strong>Tikrink, kaip nori, kad tavo profilis ir įrašai būtų randami.</strong> Įjungus įvairias Mastodon funkcijas, jos gali padėti pasiekti platesnę auditoriją. Akimirką peržiūrėk šiuos nustatymus, kad įsitikintum, jog jie atitinka tavo naudojimo būdą." remote_follow: missing_resource: Jūsų paskyros nukreipimo URL nerasta scheduled_statuses: @@ -515,6 +519,11 @@ lt: public_long: Visi gali matyti unlisted: Neįtrauktas į sąrašus unlisted_long: Matyti gali visi, tačiau nėra įtraukti į viešąsias laiko skales + statuses_cleanup: + enabled_hint: Automatiškai ištrina įrašus, kai jie pasiekia nustatytą amžiaus ribą, nebent jie atitinka vieną iš toliau nurodytų išimčių + keep_polls_hint: Neištrina jokių tavo apklausų + keep_self_bookmark: Laikyti įrašus, kuriuos pažymėjai + keep_self_bookmark_hint: Neištrina tavo pačių įrašų, jei esi juos pažymėjęs (-usi) stream_entries: sensitive_content: Jautrus turinys themes: @@ -551,22 +560,31 @@ lt: explanation: Štai keletas patarimų, kaip pradėti final_action: Pradėti kelti įrašus final_step: 'Pradėk skelbti! Net jei ir neturi sekėjų, tavo viešus įrašus gali matyti kiti, pavyzdžiui, vietinėje laiko skalėje arba saitažodžiuose. Galbūt norėsi prisistatyti saitažodyje #introductions.' - full_handle: Jūsų pilnas slapyvardis - full_handle_hint: Štai ką jūs sakytumėte savo draugams, kad jie galėtų jums siųsti žinutes arba just sekti iš kitų serverių. + full_handle: Tavo pilnas slapyvardis + full_handle_hint: Štai ką pasakytum savo draugams, kad jie galėtų parašyti arba sekti tave iš kito serverio. subject: Sveiki atvykę į Mastodon title: Sveiki atvykę, %{name}! users: - follow_limit_reached: Negalite sekti daugiau nei %{limit} žmonių + follow_limit_reached: Negali sekti daugiau nei %{limit} žmonių + go_to_sso_account_settings: Eik į savo tapatybės teikėjo paskyros nustatymus invalid_otp_token: Netinkamas dviejų veiksnių kodas otp_lost_help_html: Jei praradai prieigą prie abiejų, gali susisiek su %{email} - seamless_external_login: Jūs esate prisijungę per išorini įrenginį, todėl slaptąžodis ir el pašto nustatymai neprieinami. + seamless_external_login: Esi prisijungęs (-usi) per išorinę paslaugą, todėl slaptažodžio ir el. pašto nustatymai nepasiekiami. signed_in_as: 'Prisijungta kaip:' verification: + extra_instructions_html: <strong>Patarimas:</strong> nuoroda tavo svetainėje gali būti nematoma. Svarbi dalis – tai, kas <code>rel="me"</code> neleidžia apsimesti interneto svetainėse, kuriose yra naudotojų sukurto turinio. Vietoj to gali naudoti net <code>nuorodą</code> puslapio antraštėje esančią žymę <code>a</code>, tačiau HTML turi būti pasiekiamas nevykdant JavaScript. hint_html: "<strong>Savo tapatybės patvirtinimas Mastodon skirtas visiems.</strong> Remiantis atviraisiais žiniatinklio standartais, dabar ir visam laikui nemokamas. Viskas, ko tau reikia, yra asmeninė svetainė, pagal kurią žmonės tave atpažįsta. Kai iš savo profilio pateiksi nuorodą į šią svetainę, patikrinsime, ar svetainėje yra nuoroda į tavo profilį, ir parodysime vizualinį indikatorių." + instructions_html: Nukopijuok ir įklijuok toliau pateiktą kodą į savo svetainės HTML. Tada į vieną iš papildomų profilio laukų skirtuke „Redaguoti profilį“ įrašyk savo svetainės adresą ir išsaugok pakeitimus. verification: Patvirtinimas verified_links: Tavo patikrintos nuorodos webauthn_credentials: create: error: Kilo problema pridedant saugumo raktą. Bandyk dar kartą. + success: Tavo saugumo raktas buvo sėkmingai pridėtas. + delete_confirmation: Ar tikrai nori ištrinti šį saugumo raktą? + description_html: Jei įjungsi <strong>saugumo rakto tapatybės nustatymą</strong>, prisijungiant reikės naudoti vieną iš savo saugumo raktų. + destroy: + error: Kilo problema ištrinant saugumo raktą. Bandyk dar kartą. + success: Tavo saugumo raktas buvo sėkmingai ištrintas. nickname_hint: Įvesk naujojo saugumo rakto slapyvardį not_enabled: Dar neįjungei WebAuthn diff --git a/config/locales/ne.yml b/config/locales/ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/ry.yml b/config/locales/ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/sc.yml b/config/locales/sc.yml index fa7603f2b..d92b64783 100644 --- a/config/locales/sc.yml +++ b/config/locales/sc.yml @@ -5,6 +5,7 @@ sc: contact_missing: No cunfiguradu contact_unavailable: No a disponimentu hosted_on: Mastodon allogiadu in %{domain} + title: Informatziones accounts: follow: Sighi followers: @@ -45,6 +46,7 @@ sc: confirm: Cunfirma confirmed: Cunfirmadu confirming: Cunfirmende + custom: Personalizadu delete: Cantzella datos deleted: Cantzelladu demote: Degrada @@ -81,7 +83,9 @@ sc: moderation: active: Ativu all: Totus + disabled: Disativadu pending: De imbiare + silenced: Limitadu suspended: Suspèndidu title: Moderatzione moderation_notes: Notas de moderatzione @@ -112,6 +116,7 @@ sc: search: Chirca search_same_email_domain: Àteras persones cun su pròpiu domìniu de posta search_same_ip: Àteras persones cun sa pròpiu IP + security: Seguresa sensitive: Sensìbile sensitized: marcadu comente a sensìbile shared_inbox_url: URL de intrada cumpartzida @@ -122,6 +127,7 @@ sc: silenced: Limitadas statuses: Tuts subscribe: Sutascrie·ti + suspend: Suspensione suspended: Suspèndidu suspension_irreversible: Is datos de custu contu sunt istados cantzellados in manera irreversìbile. Podes bogare sa suspensione a su contu pro chi si potzat impreare, ma no at a recuperare datu perunu de is chi teniat in antis. suspension_reversible_hint_html: Su contu est istadu suspèndidu, e is datos ant a èssere cantzelladu de su totu su %{date}. Finas a tando, su contu si podet ripristinare sena efetu malu perunu. Si boles cantzellare totu is datos de su contu immediatamente ddu podes fàghere inoghe in bassu. @@ -274,6 +280,7 @@ sc: updated_msg: Emoji atualizadu upload: Càrriga dashboard: + media_storage: Immagasinamentu software: Programmas space: Impreu de ispàtziu title: Pannellu @@ -281,21 +288,28 @@ sc: add_new: Permite sa federatzione cun domìniu created_msg: Sa federatzione cun su domìniu est istada permìtida destroyed_msg: Sa federatzione cun su domìniu no est istada permìtida + import: Importatzione undo: Non permitas sa federatzione cun su domìniu domain_blocks: add_new: Agiunghe blocu de domìniu nou + confirm_suspension: + cancel: Annulla + confirm: Suspensione created_msg: Protzessende su blocu de domìniu destroyed_msg: Su blocu de domìniu est istadu iscontzadu domain: Domìniu edit: Modìfica su blocu de su domìniu existing_domain_block_html: As giai impostu lìmites prus astrintos a %{name}, ddu dias dèpere <a href="%{unblock_url}">isblocare</a> prima. + import: Importatzione new: create: Crea unu blocu hint: Su blocu de domìniu no at a impedire sa creatzione de contos noos in sa base de datos, ma ant a èssere aplicados in manera retroativa mètodos de moderatzione ispetzìficos subra custos contos. severity: noop: Perunu + silence: A sa muda suspend: Suspensione title: Blocu de domìniu nou + not_permitted: Non tenes su permissu de fàghere custa atzione obfuscate: Cua su nòmine de domìniu obfuscate_hint: Cua una parte de su nòmine de domìniu in sa lista si sa visualizatzione de sa lista de domìnios limitados est ativa private_comment: Cummentu privadu @@ -326,9 +340,24 @@ sc: title: Cussìgios de sighidura unsuppress: Recùpera su cussìgiu de sighidura instances: + back_to_all: Totus + back_to_limited: Limitadu + back_to_warning: Atentzione by_domain: Domìniu + content_policies: + policies: + reject_reports: Refuda informes + silence: A sa muda + suspend: Suspensione + dashboard: + instance_reports_measure: informes a subra de àtere + delivery: + all: Totus delivery_available: Sa cunsigna est a disponimentu empty: Perunu domìniu agatadu. + known_accounts: + one: "%{count} contu connòschidu" + other: "%{count} contos connòschidos" moderation: all: Totus limited: Limitadas @@ -390,18 +419,23 @@ sc: notes: one: "%{count} nota" other: "%{count} notas" + action_log: Registru de controllu action_taken_by: Mesuras adotadas dae are_you_sure: Seguru? assign_to_self: Assigna a mie assigned: Moderatzione assignada by_target_domain: Domìniu de su contu signaladu + cancel: Annulla comment: none: Perunu + confirm: Cunfirma created_at: Sinnaladu forwarded: Torradu a imbiare forwarded_to: Torradu a imbiare a %{domain} mark_as_resolved: Marca comente a isòrvidu + mark_as_sensitive: Signala comente a sensìbile mark_as_unresolved: Marcare comente a non isòrvidu + no_one_assigned: Nemos notes: create: Agiunghe una nota create_and_resolve: Isorve cun una nota @@ -419,6 +453,15 @@ sc: unassign: Boga s'assignatzione unresolved: No isòrvidu updated_at: Atualizadu + view_profile: Visualiza profilu + roles: + categories: + administration: Amministratzione + invites: Invitos + moderation: Moderatzione + delete: Cantzella + privileges: + administrator: Amministratzione rules: add_new: Agiunghe règula delete: Cantzella @@ -427,10 +470,26 @@ sc: empty: Peruna règula de serbidore definida ancora. title: Règulas de su serbidore settings: + about: + manage_rules: Gesti is règulas de su serbidore + title: Informatziones + appearance: + title: Aspetu + default_noindex: + desc_html: Ìmplicat a totu is utentes chi no apant modificadu custa cunfiguratzione + title: Esclude in manera predefinida is utentes dae s'inditzamentu de is motores de chirca + discovery: + follow_recommendations: Cussìgios de sighidura + profile_directory: Diretòriu de profilos + public_timelines: Lìnias de tempos pùblicas + title: Iscoberta + trends: Tendèntzias domain_blocks: all: Pro totus disabled: Pro nemos users: Pro utentes locales in lìnia + registrations: + title: Registros registrations_mode: modes: approved: Aprovatzione rechesta pro si registrare @@ -439,7 +498,10 @@ sc: site_uploads: delete: Cantzella s'archìviu carrigadu destroyed_msg: Càrriga de su situ cantzellada. + software_updates: + documentation_link: Àteras informatziones statuses: + application: Aplicatzione back_to_account: Torra a sa pàgina de su contu deleted: Cantzelladu media: @@ -447,6 +509,10 @@ sc: no_status_selected: Perunu istadu est istadu mudadu dae chi non nd'as seletzionadu title: Istados de su contu with_media: Cun elementos multimediales + strikes: + actions: + none: "%{name} at imbiadu un'avisu a %{target}" + suspend: "%{name} at suspèndidu su contu de %{target}" system_checks: database_schema_check: message_html: Ddoe at tràmudas de base de datos in suspesu. Pone·ddas in esecutzione pro ti assegurare chi s'aplicatzione funtzionet comente si tocat @@ -459,12 +525,24 @@ sc: review: Revisiona s'istadu updated_msg: Cunfiguratzione de etichetas atualizada title: Amministratzione + trends: + pending_review: De revisionare + tags: + title: Etichetas de tendèntzia + title: Tendèntzias warning_presets: add_new: Agiunghe noa delete: Cantzella edit_preset: Modìfica s'avisu predefinidu empty: No as cunfiguradu ancora perunu avisu predefinidu. title: Gesti is cunfiguratziones predefinidas de is avisos + webhooks: + delete: Cantzella + disable: Disativa + disabled: Disativadu + enable: Ativa + enabled: Ativu + status: Istadu admin_mailer: new_pending_account: body: Is detàllios de su contu nou sunt a suta. Podes aprovare o refudare custa rechesta. @@ -473,6 +551,9 @@ sc: body: "%{reporter} at sinnaladu %{target}" body_remote: Una persone de su domìniu %{domain} at sinnaladu %{target} subject: Informe nou pro %{instance} (#%{id}) + new_trends: + new_trending_tags: + title: Etichetas de tendèntzia aliases: add_new: Crea unu nomìngiu created_msg: Nomìngiu creadu. Immoe podes cumintzare a tramudare dae su contu betzu. @@ -495,17 +576,21 @@ sc: notification_preferences: Muda is preferèntzias de posta salutation: "%{name}," settings: 'Muda is preferèntzias de posta: %{link}' + unsubscribe: Annulla sa sutiscritzione view: 'Visualizatzione:' view_profile: Visualiza profilu view_status: Ammustra s'istadu applications: created: Aplicatzione creada destroyed: Aplicatzione cantzellada + logout: Essi regenerate_token: Torra a generare s'identificadore de atzessu token_regenerated: Identificadore de atzessu generadu warning: Dae cara a custos datos. Non ddos cumpartzas mai cun nemos! your_token: S'identificadore tuo de atzessu auth: + confirmations: + login_link: intra delete_account: Cantzella su contu delete_account_html: Si boles cantzellare su contu, ddu podes <a href="%{path}">fàghere inoghe</a>. T'amus a dimandare una cunfirmatzione. description: @@ -528,11 +613,14 @@ sc: register: Registru registration_closed: "%{instance} no atzetat àteras persones" reset_password: Reseta sa crae + rules: + back: A coa security: Seguresa set_new_password: Cunfigura una crae noa status: account_status: Istadu de su contu confirming: Isetende chi sa posta eletrònica siat cumpletada. + functional: Su contu tuo est operativu. pending: Sa dimanda tua est in protzessu de revisione dae su personale nostru. Podet serbire unu pagu de tempus. As a retzire unu messàgiu eletrònicu si sa dimanda est aprovada. redirecting_to: Su contu tuo est inativu pro ite in die de oe est torrende a indiritzare a %{acct}. too_fast: Formulàriu imbiadu tropu a lestru, torra a proare. @@ -581,8 +669,14 @@ sc: more_details_html: Pro àteros detàllios, bide sa <a href="%{terms_path}">normativa de riservadesa</a>. username_available: Su nòmine de utente tuo at a torrare a èssere a disponimentu username_unavailable: Su nòmine de utente tuo no at a abarrare a disponimentu + disputes: + strikes: + title_actions: + none: Atentzione domain_validator: invalid_domain: no est unu nòmine de domìniu vàlidu + edit_profile: + other: Àteru errors: '400': Sa dimanda chi as imbiadu non fiat vàlida o non fiat curreta. '403': Non tenes permissu pro bìdere custa pàgina. @@ -638,11 +732,15 @@ sc: title: Agiunghe unu filtru nou generic: all: Totus + cancel: Annulla changes_saved_msg: Modìficas sarvadas. + confirm: Cunfirma copy: Còpia delete: Cantzella + none: Perunu order_by: Òrdina pro save_changes: Sarva is modìficas + today: oe validation_errors: one: Calicuna cosa ancora no est andende. Bide sa faddina in bàsciu other: Calicuna cosa ancora no est andende. Bide is %{count} faddinas in bàsciu @@ -655,12 +753,15 @@ sc: overwrite: Subrascrie overwrite_long: Sostitui is registros atuales cun cussos noos preface: Podes importare datos chi as esportadu dae unu àteru serbidore, che a sa lista de sa gente chi ses sighende o blochende. + status: Istadu success: Datos carrigados; ant a èssere protzessados luego + type: Casta de importatzione types: blocking: Lista de blocos bookmarks: Sinnalibros domain_blocking: Lista domìnios blocados following: Lista de sighiduras + lists: Listas muting: Lista gente a sa muda upload: Càrriga invites: @@ -685,6 +786,13 @@ sc: expires_at: Iscadit uses: Impreos title: Invita gente + login_activities: + authentication_methods: + password: crae + webauthn: craes de seguresa + mail_subscriptions: + unsubscribe: + title: Annulla sa sutiscritzione media_attachments: validations: images_and_video: Non si podet allegare unu vìdeu in una publicatzione chi cuntenet giai immàgines @@ -797,6 +905,8 @@ sc: other: Àteru posting_defaults: Valores predefinidos de publicatzione public_timelines: Lìnias de tempos pùblicas + privacy: + search: Chirca reactions: errors: limit_reached: Lìmite de reatziones diferentes cròmpidu @@ -850,6 +960,7 @@ sc: platforms: adobe_air: Adobe Air android: Android + chrome_os: ChromeOS firefox_os: Firefox OS ios: iOS linux: Linux @@ -934,6 +1045,7 @@ sc: '2629746': 1 mese '31556952': 1 annu '5259492': 2 meses + '604800': 1 chida '63113904': 2 annos '7889238': 3 meses stream_entries: @@ -969,6 +1081,7 @@ sc: subject: S'archìviu tuo est prontu pro èssere iscarrigadu title: Collida dae s'archìviu warning: + reason: 'Resone:' subject: disable: Su contu tuo %{acct} est istadu cungeladu none: Avisu pro %{acct} diff --git a/config/locales/simple_form.fil.yml b/config/locales/simple_form.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/simple_form.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index 39caaf6ba..d53b7105e 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -27,14 +27,33 @@ lt: none: Naudok šią parinktį norėdamas (-a) išsiųsti įspėjimą naudotojui, nesukeldamas (-a) jokio kito veiksmo. sensitive: Priversk visus šio naudotojo medijos priedus pažymėti kaip jautrius. silence: Neleisk naudotojui skelbti viešai matomų įrašų, paslėpk jų įrašus ir pranešimus nuo žmonių, kurie neseka jo. Uždaro visus su šia paskyra susijusius ataskaitas. + suspend: Neleisk jokios sąveikos iš šios paskyros arba į ją ir ištrink jos turinį. Sugrąžinama per 30 dienų. Uždaro visas su šia paskyra susijusias ataskaitas. + warning_preset_id: Pasirinktinai. Gali pridėti pasirinktinį tekstą iš anksto nustatyto rinkinio pabaigoje + announcement: + all_day: Jei pažymėta, bus rodomos tik laikotarpio datos + ends_at: Pasirinktinai. Skelbimas šiuo laiku bus automatiškai panaikintas + scheduled_at: Palik tuščią, kad skelbimas būtų paskelbtas iš karto + starts_at: Pasirinktinai. Jei skelbimas susietas su tam tikru laiko tarpu + text: Gali naudoti įrašo sintaksę. Būk dėmesingas (-a), kiek vietos naudotojo ekrane užims skelbimas + appeal: + text: Gali pateikti apeliaciją dėl streiko tik vieną kartą defaults: + autofollow: Žmonės, kurie užsiregistruos per kvietimą, automatiškai seks tave avatar: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions} tšk. - header: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions}tšk. + bot: Signalizuoti kitiems, kad paskyroje daugiausia atliekami automatiniai veiksmai ir kad ji gali būti nestebima + context: Vienas arba keli kontekstai, kuriems turėtų būti taikomas filtras + current_password: Saugumo sumetimais įvesk dabartinės paskyros slaptažodį + current_username: Kad patvirtintum, įvesk dabartinės paskyros naudotojo vardą + digest: Siunčiama tik po ilgo neaktyvumo laikotarpio ir tik tuo atveju, jei negavai jokių asmeninių žinučių + email: Tau bus išsiųstas patvirtinimo el. laiškas + header: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions} tšk. inbox_url: Nukopijuok URL adresą iš pradinio puslapio perdavėjo, kurį nori naudoti irreversible: Filtruoti įrašai išnyks negrįžtamai, net jei vėliau filtras bus pašalintas locale: Naudotojo sąsajos kalba, el. laiškai ir stumiamieji pranešimai password: Naudok bent 8 simbolius - phrase: Bus suderinta, neatsižvelgiant į teksto korpusą arba įrašo turinio įspėjimą + phrase: Bus suderinta, neatsižvelgiant į teksto lygį arba įrašo turinio įspėjimą + scopes: Prie kurių API programai bus leidžiama pasiekti. Pasirinkus aukščiausio lygio sritį, atskirų sričių pasirinkti nereikia. + setting_aggregate_reblogs: Nerodyti naujų pakėlimų įrašams, kurie neseniai buvo pakelti (taikoma tik naujai gautiems pakėlimams) setting_always_send_emails: Paprastai pranešimai el. paštu nebus siunčiami, kai aktyviai naudoji Mastodon setting_default_sensitive: Jautrioji medija pagal numatytuosius nustatymus yra paslėpta ir gali būti atskleista paspaudus setting_display_media_default: Slėpti mediją, pažymėtą kaip jautrią @@ -42,16 +61,26 @@ lt: setting_display_media_show_all: Visada rodyti mediją setting_use_blurhash: Gradientai pagrįsti paslėptų vaizdų spalvomis, tačiau užgožia bet kokias detales setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio kanalo slinkimo + username: Gali naudoti raides, skaičius ir pabraukimus + whole_word: Kai raktažodis ar frazė yra tik raidinis ir skaitmeninis, jis bus taikomas tik tada, jei atitiks visą žodį featured_tag: name: 'Štai keletas pastaruoju metu dažniausiai saitažodžių, kurių tu naudojai:' + filters: + action: Pasirink, kokį veiksmą atlikti, kai įrašas atitinka filtrą + actions: + hide: Visiškai paslėpti filtruotą turinį ir elgtis taip, tarsi jo neegzistuotų + warn: Slėpti filtruojamą turinį po įspėjimu, paminint filtro pavadinimą form_admin_settings: + activity_api_enabled: Vietinių paskelbtų įrašų, aktyvių naudotojų ir naujų registracijų skaičiai kas savaitę + backups_retention_period: Laikyti sukurtus naudotojų archyvus nurodytą dienų skaičių. peers_api_enabled: Domenų pavadinimų sąrašas, su kuriais šis serveris susidūrė fediverse. Čia nėra duomenų apie tai, ar tu bendrauji su tam tikru serveriu, tik apie tai, kad tavo serveris apie jį žino. Tai naudojama tarnybose, kurios renka federacijos statistiką bendrąja prasme. site_contact_email: Kaip žmonės gali su tavimi susisiekti teisiniais ar pagalbos užklausimais. site_contact_username: Kaip žmonės gali tave pasiekti Mastodon. site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę. trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo. sessions: - webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paliesk. + otp: 'Įvesk telefono programėlėje sugeneruotą dviejų tapatybės kodą arba naudok vieną iš atkūrimo kodų:' + webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paspausk. settings: indexable: Tavo profilio puslapis gali būti rodomas paieškos rezultatuose Google, Bing ir kituose. labels: diff --git a/config/locales/simple_form.ne.yml b/config/locales/simple_form.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/simple_form.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/simple_form.ry.yml b/config/locales/simple_form.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/simple_form.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/simple_form.sc.yml b/config/locales/simple_form.sc.yml index 2c4725996..5f5d63307 100644 --- a/config/locales/simple_form.sc.yml +++ b/config/locales/simple_form.sc.yml @@ -53,6 +53,8 @@ sc: domain: Custu domìniu at a pòdere recuperare datos dae custu serbidore e is datos in intrada dae cue ant a èssere protzessados e archiviados email_domain_block: with_dns_records: S'at a fàghere unu tentativu de risòlvere is registros DNS de su domìniu e fintzas is risultados ant a èssere blocados + form_admin_settings: + activity_api_enabled: Nùmeru de tuts publicados in locale, utentes ativos e registros noos in perìodos chidajolos form_challenge: current_password: Ses intrende in un'àrea segura imports: @@ -155,6 +157,7 @@ sc: setting_use_pending_items: Modalidade lenta severity: Severidade sign_in_token_attempt: Còdighe de seguresa + title: Tìtulu type: Casta de importatzione username: Nòmine utente username_or_email: Nòmine utente o indiritzu de posta eletrònica @@ -163,6 +166,16 @@ sc: with_dns_records: Include registros MX e indiritzos IP de su domìniu featured_tag: name: Eticheta + form_admin_settings: + activity_api_enabled: Pùblica istatìsticas agregadas subra s'atividade de s'utente + custom_css: CSS personalizadu + peers_api_enabled: Pùblica sa lista de serbidores iscobertos in s'API + profile_directory: Ativa diretòriu de profilos + show_domain_blocks: Ammustra blocos de domìniu + site_contact_username: Nòmine de utente de su cuntatu + site_short_description: Descritzione de su serbidore + site_title: Nòmine de su serbidore + thumbnail: Miniadura de su serbidore interactions: must_be_follower: Bloca is notìficas dae chie non ti sighit must_be_following: Bloca is notìficas dae gente chi non sighis @@ -186,6 +199,7 @@ sc: mention: Una persone t'at mentovadu pending_account: Unu contu nou tenet bisòngiu de una revisione reblog: Una persone at cumpartzidu s'istadu tuo + report: Imbiu de un'informe nou rule: text: Règula tag: @@ -193,6 +207,9 @@ sc: name: Eticheta trendable: Permite a custa eticheta de apàrrere in is tendèntzias usable: Permite a is tuts de impreare custa eticheta + user_role: + name: Nòmine + permissions_as_keys: Permissos 'no': Nono recommended: Cussigiadu required: diff --git a/config/locales/simple_form.tlh.yml b/config/locales/simple_form.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/simple_form.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/tlh.yml b/config/locales/tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/tlh.yml @@ -0,0 +1 @@ +tlh: From 996c13a24d941e02458fa21d30e8334d04bdb2b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:33:11 +0100 Subject: [PATCH 19/73] Update dependency core-js to v3.34.0 (#28241) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4226fcc0f..9e1622680 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5960,9 +5960,9 @@ __metadata: linkType: hard "core-js@npm:^3.30.2": - version: 3.33.3 - resolution: "core-js@npm:3.33.3" - checksum: 08abdc9470c8228b9d09f61e62ab312738681202c4c34e9638889125b304b235f34c4fe22e9d41c20906ac0fcc807dca57c5ff7d6b90021bf64e8fe23461d9ab + version: 3.34.0 + resolution: "core-js@npm:3.34.0" + checksum: 408a77898abe03bf3e5dec2a451c36f4745081cca9022f8bdf9b817d57bb6d3a534d555f47a4b95e1daa5e21dbc79122eac2402e25720d425f5925127e55dcd8 languageName: node linkType: hard From 3b710b96cf4a7ff743313f20b054c6220da3dd6b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:33:27 +0100 Subject: [PATCH 20/73] Update dependency irb to v1.10.1 (#28240) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9308a41c8..4a409a0ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -377,7 +377,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.5) io-console (0.6.0) - irb (1.10.0) + irb (1.10.1) rdoc reline (>= 0.3.8) jmespath (1.6.2) @@ -608,7 +608,7 @@ GEM link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.6.1) rdf (~> 3.2) - rdoc (6.6.0) + rdoc (6.6.1) psych (>= 4.0.0) redcarpet (3.6.0) redis (4.8.1) From faffd81976092fc5a95fb359ec5844c2af76101d Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 03:44:07 -0500 Subject: [PATCH 21/73] Remove double subject call in `services/unsuspend_account_service` spec (#28215) --- .../unsuspend_account_service_spec.rb | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb index c555b661e..2f737c621 100644 --- a/spec/services/unsuspend_account_service_spec.rb +++ b/spec/services/unsuspend_account_service_spec.rb @@ -45,14 +45,19 @@ RSpec.describe UnsuspendAccountService, type: :service do remote_follower.follow!(account) end - it "merges back into local followers' feeds" do + it 'merges back into feeds of local followers and sends update' do subject + + expect_feeds_merged + expect_updates_sent + end + + def expect_feeds_merged expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) end - it 'sends an update actor to followers and reporters' do - subject + def expect_updates_sent expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once end @@ -73,19 +78,20 @@ RSpec.describe UnsuspendAccountService, type: :service do allow(resolve_account_service).to receive(:call).with(account).and_return(account) end - it 're-fetches the account' do - subject + it 're-fetches the account, merges feeds, and preserves suspended' do + expect { subject } + .to_not change_suspended_flag + expect_feeds_merged expect(resolve_account_service).to have_received(:call).with(account) end - it "merges back into local followers' feeds" do - subject + def expect_feeds_merged expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) end - it 'does not change the “suspended” flag' do - expect { subject }.to_not change(account, :suspended?) + def change_suspended_flag + change(account, :suspended?) end end @@ -97,19 +103,20 @@ RSpec.describe UnsuspendAccountService, type: :service do end end - it 're-fetches the account' do - subject + it 're-fetches the account, does not merge feeds, marks suspended' do + expect { subject } + .to change_suspended_to_true expect(resolve_account_service).to have_received(:call).with(account) + expect_feeds_not_merged end - it "does not merge back into local followers' feeds" do - subject + def expect_feeds_not_merged expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) end - it 'marks account as suspended' do - expect { subject }.to change(account, :suspended?).from(false).to(true) + def change_suspended_to_true + change(account, :suspended?).from(false).to(true) end end @@ -118,13 +125,14 @@ RSpec.describe UnsuspendAccountService, type: :service do allow(resolve_account_service).to receive(:call).with(account).and_return(nil) end - it 're-fetches the account' do + it 're-fetches the account and does not merge feeds' do subject + expect(resolve_account_service).to have_received(:call).with(account) + expect_feeds_not_merged end - it "does not merge back into local followers' feeds" do - subject + def expect_feeds_not_merged expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) end From 5517df61de1e867afa531268755ee893ffca3c99 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 03:44:51 -0500 Subject: [PATCH 22/73] Remove double subject call in `services/activitypub/process_account_service` spec (#28214) --- .../process_account_service_spec.rb | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index c02a0800a..09eb5ddee 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -129,12 +129,10 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do stub_const 'ActivityPub::ProcessAccountService::SUBDOMAINS_RATELIMIT', 5 end - it 'creates at least some accounts' do - expect { subject }.to change { Account.remote.count }.by_at_least(2) - end - - it 'creates no more account than the limit allows' do - expect { subject }.to change { Account.remote.count }.by_at_most(5) + it 'creates accounts without exceeding rate limit' do + expect { subject } + .to create_some_remote_accounts + .and create_fewer_than_rate_limit_accounts end end @@ -195,12 +193,20 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end end - it 'creates at least some accounts' do - expect { subject.call('user1', 'foo.test', payload) }.to change { Account.remote.count }.by_at_least(2) - end - - it 'creates no more account than the limit allows' do - expect { subject.call('user1', 'foo.test', payload) }.to change { Account.remote.count }.by_at_most(5) + it 'creates accounts without exceeding rate limit' do + expect { subject.call('user1', 'foo.test', payload) } + .to create_some_remote_accounts + .and create_fewer_than_rate_limit_accounts end end + + private + + def create_some_remote_accounts + change(Account.remote, :count).by_at_least(2) + end + + def create_fewer_than_rate_limit_accounts + change(Account.remote, :count).by_at_most(5) + end end From be6bb1a10d6f1f23151198c6487f44145a0692ee Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 03:45:19 -0500 Subject: [PATCH 23/73] Remove double subject call in `services/suspend_account_service` spec (#28213) --- spec/services/suspend_account_service_spec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index edb705008..c258995b7 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -18,14 +18,15 @@ RSpec.describe SuspendAccountService, type: :service do account.suspend! end - it "unmerges from local followers' feeds" do - subject + it 'unmerges from feeds of local followers and preserves suspended flag' do + expect { subject } + .to_not change_suspended_flag expect(FeedManager.instance).to have_received(:unmerge_from_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:unmerge_from_list).with(account, list) end - it 'does not change the “suspended” flag' do - expect { subject }.to_not change(account, :suspended?) + def change_suspended_flag + change(account, :suspended?) end end From ed7b5c091b62d63e695d2f1ff946e021fb37fa18 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 03:51:09 -0500 Subject: [PATCH 24/73] Remove double subject call in `services/delete_account_service` spec (#28212) --- spec/services/delete_account_service_spec.rb | 25 +++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index 68ab491e4..8a19d3cf7 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -27,8 +27,15 @@ RSpec.describe DeleteAccountService, type: :service do let!(:account_note) { Fabricate(:account_note, account: account) } - it 'deletes associated owned records' do - expect { subject }.to change { + it 'deletes associated owned and target records and target notifications' do + expect { subject } + .to delete_associated_owned_records + .and delete_associated_target_records + .and delete_associated_target_notifications + end + + def delete_associated_owned_records + change do [ account.statuses, account.media_attachments, @@ -39,23 +46,23 @@ RSpec.describe DeleteAccountService, type: :service do account.polls, account.account_notes, ].map(&:count) - }.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) + end.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) end - it 'deletes associated target records' do - expect { subject }.to change { + def delete_associated_target_records + change do [ AccountPin.where(target_account: account), ].map(&:count) - }.from([1]).to([0]) + end.from([1]).to([0]) end - it 'deletes associated target notifications' do - expect { subject }.to change { + def delete_associated_target_notifications + change do %w( poll favourite status mention follow ).map { |type| Notification.where(type: type).count } - }.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0]) + end.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0]) end end From 0e8ba19113182f74a0adde0ac75a35694833316c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 03:52:30 -0500 Subject: [PATCH 25/73] Add spec coverage for `CLI::Emoji` class (#28182) --- spec/fixtures/files/elite-assets.tar.gz | Bin 0 -> 17590 bytes spec/lib/mastodon/cli/emoji_spec.rb | 54 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 spec/fixtures/files/elite-assets.tar.gz diff --git a/spec/fixtures/files/elite-assets.tar.gz b/spec/fixtures/files/elite-assets.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..7b4f4425705c62fe4b538fa52148937e51a69552 GIT binary patch literal 17590 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 literal 0 HcmV?d00001 diff --git a/spec/lib/mastodon/cli/emoji_spec.rb b/spec/lib/mastodon/cli/emoji_spec.rb index 5d109eb52..530da91e7 100644 --- a/spec/lib/mastodon/cli/emoji_spec.rb +++ b/spec/lib/mastodon/cli/emoji_spec.rb @@ -4,5 +4,59 @@ require 'rails_helper' require 'mastodon/cli/emoji' describe Mastodon::CLI::Emoji do + subject { cli.invoke(action, args, options) } + + let(:cli) { described_class.new } + let(:args) { [] } + let(:options) { {} } + it_behaves_like 'CLI Command' + + describe '#purge' do + let(:action) { :purge } + + context 'with existing custom emoji' do + before { Fabricate(:custom_emoji) } + + it 'reports a successful purge' do + expect { subject } + .to output_results('OK') + end + end + end + + describe '#import' do + context 'with existing custom emoji' do + let(:import_path) { Rails.root.join('spec', 'fixtures', 'files', 'elite-assets.tar.gz') } + let(:action) { :import } + let(:args) { [import_path] } + + it 'reports about imported emoji' do + expect { subject } + .to output_results('Imported 1') + .and change(CustomEmoji, :count).by(1) + end + end + end + + describe '#export' do + context 'with existing custom emoji' do + before { Fabricate(:custom_emoji) } + after { File.delete(export_path) } + + let(:export_path) { Rails.root.join('tmp', 'export.tar.gz') } + let(:args) { [Rails.root.join('tmp')] } + let(:action) { :export } + + it 'reports about exported emoji' do + expect { subject } + .to output_results('Exported 1') + .and change { File.exist?(export_path) }.from(false).to(true) + end + end + end + + def output_results(string) + output(a_string_including(string)).to_stdout + end end From 954169966b23214a044fa8deda16be4b903526d5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 08:52:55 +0000 Subject: [PATCH 26/73] New Crowdin Translations (automated) (#28245) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/lt.json | 26 +++---- config/locales/simple_form.lt.yml | 91 ++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index e588b8538..58c80e411 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -40,7 +40,7 @@ "account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.", "account.follows_you": "Seka tave", "account.go_to_profile": "Eiti į profilį", - "account.hide_reblogs": "Slėpti \"boosts\" iš @{name}", + "account.hide_reblogs": "Slėpti pakėlimus iš @{name}", "account.in_memoriam": "Atminimui.", "account.joined_short": "Prisijungė", "account.languages": "Keisti prenumeruojamas kalbas", @@ -49,19 +49,19 @@ "account.media": "Medija", "account.mention": "Paminėti @{name}", "account.moved_to": "{name} nurodė, kad dabar jų nauja paskyra yra:", - "account.mute": "Užtildyti @{name}", + "account.mute": "Nutildyti @{name}", "account.mute_notifications_short": "Nutildyti pranešimus", "account.mute_short": "Nutildyti", - "account.muted": "Užtildytas", + "account.muted": "Nutildytas", "account.no_bio": "Nėra pateikto aprašymo.", - "account.open_original_page": "Atidaryti originalinį tinklalapį", + "account.open_original_page": "Atidaryti originalinį puslapį", "account.posts": "Įrašai", "account.posts_with_replies": "Įrašai ir atsakymai", "account.report": "Pranešti @{name}", - "account.requested": "Laukiama patvirtinimo. Spausk, kad atšaukti sekimo užklausą.", + "account.requested": "Laukiama patvirtinimo. Spausk, kad atšaukti sekimo užklausą", "account.requested_follow": "{name} paprašė tave sekti", "account.share": "Bendrinti @{name} profilį", - "account.show_reblogs": "Rodyti \"boosts\" iš @{name}", + "account.show_reblogs": "Rodyti pakėlimus iš @{name}", "account.statuses_counter": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}}", "account.unblock": "Atblokuoti @{name}", "account.unblock_domain": "Atblokuoti domeną {domain}", @@ -73,7 +73,7 @@ "account.unmute_short": "Atitildyti", "account_note.placeholder": "Spausk norėdamas (-a) pridėti pastabą", "admin.dashboard.daily_retention": "Vartotojų išbuvimo rodiklis pagal dieną po registracijos", - "admin.dashboard.monthly_retention": "Vartotojų išbuvimo rodiklis pagal mėnesį po registracijos", + "admin.dashboard.monthly_retention": "Naudotojų išlaikymo rodiklis pagal mėnesį po registracijos", "admin.dashboard.retention.average": "Vidurkis", "admin.dashboard.retention.cohort": "Registravimo mėnuo", "admin.dashboard.retention.cohort_size": "Nauji naudotojai", @@ -117,9 +117,9 @@ "column.favourites": "Mėgstamiausi", "column.firehose": "Tiesioginiai padavimai", "column.follow_requests": "Sekti prašymus", - "column.home": "Pradžia", + "column.home": "Pagrindinis", "column.lists": "Sąrašai", - "column.mutes": "Užtildyti naudotojai", + "column.mutes": "Nutildyti naudotojai", "column.notifications": "Pranešimai", "column.pins": "Prisegti įrašai", "column.public": "Federacinė laiko skalė", @@ -141,13 +141,13 @@ "compose.saved.body": "Įrašas išsaugotas.", "compose_form.direct_message_warning_learn_more": "Sužinoti daugiau", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", - "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", - "compose_form.lock_disclaimer": "Jūsų paskyra nėra {locked}. Kiekvienas gali jus sekti ir peržiūrėti tik sekėjams skirtus įrašus.", + "compose_form.hashtag_warning": "Šis įrašas nebus įtraukta į jokį saitažodį, nes ji nėra vieša. Tik viešų įrašų galima ieškoti pagal saitažodį.", + "compose_form.lock_disclaimer": "Tavo paskyra nėra {locked}. Bet kas gali sekti tave ir peržiūrėti tik sekėjams skirtus įrašus.", "compose_form.lock_disclaimer.lock": "užrakinta", "compose_form.placeholder": "Kas tavo mintyse?", "compose_form.poll.add_option": "Pridėti pasirinkimą", "compose_form.poll.duration": "Apklausos trukmė", - "compose_form.poll.option_placeholder": "Pasirinkimas {number}", + "compose_form.poll.option_placeholder": "{number} pasirinkimas", "compose_form.poll.remove_option": "Pašalinti šį pasirinkimą", "compose_form.poll.switch_to_multiple": "Keisti apklausą, kad būtų galima pasirinkti kelis pasirinkimus", "compose_form.poll.switch_to_single": "Pakeisti apklausą, kad būtų galima pasirinkti vieną variantą", @@ -528,6 +528,8 @@ "search_results.hashtags": "Saitažodžiai", "search_results.nothing_found": "Nepavyko rasti nieko pagal šiuos paieškos terminus.", "search_results.statuses": "Toots", + "server_banner.about_active_users": "Žmonės, kurie naudojosi šiuo serveriu per pastarąsias 30 dienų (mėnesio aktyvūs naudotojai)", + "server_banner.active_users": "aktyvūs naudotojai", "sign_in_banner.sign_in": "Prisijungimas", "sign_in_banner.text": "Prisijunk, kad galėtum sekti profilius arba saitažodžius, mėgsti, bendrinti ir atsakyti į įrašus. Taip pat gali bendrauti iš savo paskyros kitame serveryje.", "status.admin_status": "Open this status in the moderation interface", diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index d53b7105e..6eb90340d 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -63,6 +63,8 @@ lt: setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio kanalo slinkimo username: Gali naudoti raides, skaičius ir pabraukimus whole_word: Kai raktažodis ar frazė yra tik raidinis ir skaitmeninis, jis bus taikomas tik tada, jei atitiks visą žodį + email_domain_block: + with_dns_records: Bus bandoma išspręsti nurodyto domeno DNS įrašus, o rezultatai taip pat bus blokuojami featured_tag: name: 'Štai keletas pastaruoju metu dažniausiai saitažodžių, kurių tu naudojai:' filters: @@ -77,15 +79,98 @@ lt: site_contact_email: Kaip žmonės gali su tavimi susisiekti teisiniais ar pagalbos užklausimais. site_contact_username: Kaip žmonės gali tave pasiekti Mastodon. site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę. + thumbnail: Maždaug 2:1 dydžio vaizdas, rodomas šalia tavo serverio informacijos. + timeline_preview: Atsijungę lankytojai galės naršyti naujausius viešus įrašus, esančius serveryje. trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo. sessions: otp: 'Įvesk telefono programėlėje sugeneruotą dviejų tapatybės kodą arba naudok vieną iš atkūrimo kodų:' webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paspausk. settings: indexable: Tavo profilio puslapis gali būti rodomas paieškos rezultatuose Google, Bing ir kituose. + user: + chosen_languages: Kai pažymėta, viešose laiko skalėse bus rodomi tik įrašai pasirinktomis kalbomis + role: Vaidmuo valdo, kokius leidimus naudotojas (-a) turi labels: + account: + indexable: Įtraukti viešus įrašus į paieškos rezultatus + show_collections: Rodyti sekimus ir sekėjus profilyje + unlocked: Automatiškai priimti naujus sekėjus + account_warning_preset: + title: Pavadinimas + admin_account_action: + include_statuses: Įtraukti praneštus įrašus į el. laišką + defaults: + avatar: Profilio nuotrauka + bot: Tai automatinė paskyra + chosen_languages: Filtruoti kalbas + display_name: Rodomas vardas + email: El. pašto adresas + expires_in: Nustoja galioti po + fields: Papildomi laukai + irreversible: Mesti vietoj slėpti + locale: Sąsajos kalba + max_uses: Maksimalus naudojimo skaičius + new_password: Naujas slaptažodis + note: Biografija + password: Slaptažodis + phrase: Raktažodis arba frazė + setting_auto_play_gif: Automatiškai leisti animuotų GIF + setting_boost_modal: Rodyti patvirtinimo dialogą prieš pakėliant įrašą + setting_default_language: Skelbimo kalba + setting_default_privacy: Skelbimo privatumas + setting_default_sensitive: Visada žymėti mediją kaip jautrią + setting_delete_modal: Rodyti patvirtinimo dialogą prieš ištrinant įrašą + setting_display_media: Medijos rodymas + setting_display_media_hide_all: Slėpti viską + setting_display_media_show_all: Rodyti viską + setting_expand_spoilers: Visada išplėsti įrašus, pažymėtus turinio įspėjimais + setting_hide_network: Slėpti savo socialinę diagramą + setting_system_font_ui: Naudoti numatytąjį sistemos šriftą + setting_theme: Svetainės tema + setting_use_pending_items: Lėtas režimas + title: Pavadinimas + type: Importo tipas + username: Naudotojo vardas + username_or_email: Naudotojo vardas arba el. paštas + whole_word: Visas žodis + email_domain_block: + with_dns_records: Įtraukti MX įrašus ir domeno IP adresus featured_tag: name: Saitažodis + filters: + actions: + hide: Slėpti visiškai + warn: Slėpti su įspėjimu + form_admin_settings: + activity_api_enabled: Skelbti suvestinį statistiką apie naudotojų veiklą per API + bootstrap_timeline_accounts: Visada rekomenduoti šias paskyras naujiems naudotojams + content_cache_retention_period: Turinio talpyklos išlaikymo laikotarpis + custom_css: Pasirinktinis CSS + mascot: Pasirinktinis talismanas (pasenęs) + registrations_mode: Kas gali užsiregistruoti + show_domain_blocks_rationale: Rodyti, kodėl domenai buvo užblokuoti + site_extended_description: Išplėstas aprašymas + site_short_description: Serverio aprašymas + site_terms: Privatumo politika + site_title: Serverio pavadinimas + theme: Numatytoji tema + thumbnail: Serverio miniatūra + invite_request: + text: Kodėl nori prisijungti? + notification_emails: + favourite: Kažkas pamėgo tavo įrašą + follow: Kažkas seka tave + follow_request: Kažkas paprašė sekti tave + mention: Kažkas paminėjo tave + pending_account: Reikia peržiūros naujam paskyrui + reblog: Kažkas pakėlė tavo įrašą + software_updates: + label: Yra nauja Mastodon versija + patch: Pranešti apie klaidų ištaisymo atnaujinimus + rule: + text: Taisyklė + settings: + show_application: Rodyti, iš kurios programėles išsiuntei įrašą tag: listable: Leisti šį saitažodį rodyti paieškose ir pasiūlymuose name: Saitažodis @@ -93,11 +178,15 @@ lt: usable: Leisti įrašams naudoti šį saitažodį user: role: Vaidmuo + time_zone: Laiko juosta user_role: + color: Ženklelio spalva + highlighted: Rodyti vaidmenį kaip ženklelį naudotojo profiliuose + name: Pavadinimas permissions_as_keys: Leidimai position: Prioritetas webhook: - events: Įgalinti įvykiai + events: Įjungti įvykiai template: Naudingosios apkrovos šablonas url: Galutinio taško URL 'no': Ne From 42afd303246abe1cf61752ab53cca466ea3cefdf Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 05:19:24 -0500 Subject: [PATCH 27/73] Replace Sprockets with Propshaft (#28239) --- .github/renovate.json5 | 1 - Gemfile | 3 +-- Gemfile.lock | 15 ++++++--------- config/application.rb | 1 - config/environments/development.rb | 11 ----------- config/initializers/assets.rb | 16 ---------------- 6 files changed, 7 insertions(+), 40 deletions(-) delete mode 100644 config/initializers/assets.rb diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 895dbfbad..a7998ddfd 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -50,7 +50,6 @@ matchManagers: ['bundler'], matchPackageNames: [ 'rack', // Needs to be synced with Rails version - 'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x 'strong_migrations', // Requires manual upgrade 'sidekiq', // Requires manual upgrade 'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version diff --git a/Gemfile b/Gemfile index e3fb39e16..cfcbcc0d3 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ ruby '>= 3.0.0' gem 'puma', '~> 6.3' gem 'rails', '~> 7.1.1' -gem 'sprockets', '~> 3.7.2' +gem 'propshaft' gem 'thor', '~> 1.2' gem 'rack', '~> 2.2.7' @@ -89,7 +89,6 @@ gem 'sidekiq-unique-jobs', '~> 7.1' gem 'sidekiq-bulk', '~> 0.2.0' gem 'simple-navigation', '~> 4.4' gem 'simple_form', '~> 5.2' -gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie' gem 'stoplight', '~> 3.0.1' gem 'strong_migrations', '1.6.4' gem 'tty-prompt', '~> 0.23', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 4a409a0ad..738562116 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -534,6 +534,11 @@ GEM net-smtp premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) + propshaft (0.8.0) + actionpack (>= 7.0.0) + activesupport (>= 7.0.0) + rack + railties (>= 7.0.0) psych (5.1.1.1) stringio public_suffix (5.0.4) @@ -736,13 +741,6 @@ GEM simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) smart_properties (1.17.0) - sprockets (3.7.2) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - sprockets (>= 3.0.0) stackprof (0.2.25) statsd-ruby (1.5.0) stoplight (3.0.2) @@ -911,6 +909,7 @@ DEPENDENCIES posix-spawn premailer-rails private_address_check (~> 0.5) + propshaft public_suffix (~> 5.0) puma (~> 6.3) pundit (~> 2.3) @@ -949,8 +948,6 @@ DEPENDENCIES simple_form (~> 5.2) simplecov (~> 0.22) simplecov-lcov (~> 0.8) - sprockets (~> 3.7.2) - sprockets-rails (~> 3.4) stackprof stoplight (~> 3.0.1) strong_migrations (= 1.6.4) diff --git a/config/application.rb b/config/application.rb index 99ee4ffd7..b6426516e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -14,7 +14,6 @@ require 'active_job/railtie' # require 'action_mailbox/engine' # require 'action_text/engine' # require 'rails/test_unit/railtie' -require 'sprockets/railtie' # Used to be implicitly required in action_mailbox/engine require 'mail' diff --git a/config/environments/development.rb b/config/environments/development.rb index e601fc014..3c13ada38 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -71,17 +71,6 @@ Rails.application.configure do # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true - # Debug mode disables concatenation and preprocessing of assets. - config.assets.debug = true - - # Suppress logger output for asset requests. - config.assets.quiet = true - - # Adds additional error checking when serving assets at runtime. - # Checks for improperly declared sprockets dependencies. - # Raises helpful error messages. - config.assets.raise_runtime_errors = true - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index e1fd5f8ce..000000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) - -Rails.application.config.assets.initialize_on_precompile = true From ee83d5c7600b2cdc96db78c5ec40d8747853ba30 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 08:42:12 -0500 Subject: [PATCH 28/73] Enable the eslint `react/no-unknown-property` rule (#28217) --- .eslintrc.js | 1 - .../mastodon/features/interaction_modal/index.jsx | 6 +++--- .../mastodon/features/ui/components/navigation_panel.jsx | 2 +- app/javascript/mastodon/features/video/index.jsx | 1 - package.json | 2 +- yarn.lock | 4 ++-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 176879034..e2d16a54a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -120,7 +120,6 @@ module.exports = defineConfig({ 'react/jsx-uses-react': 'off', // not needed with new JSX transform 'react/jsx-wrap-multilines': 'error', 'react/no-deprecated': 'off', - 'react/no-unknown-property': 'off', 'react/react-in-jsx-scope': 'off', // not needed with new JSX transform 'react/self-closing-comp': 'error', diff --git a/app/javascript/mastodon/features/interaction_modal/index.jsx b/app/javascript/mastodon/features/interaction_modal/index.jsx index 4f145f9ed..216c63a7e 100644 --- a/app/javascript/mastodon/features/interaction_modal/index.jsx +++ b/app/javascript/mastodon/features/interaction_modal/index.jsx @@ -298,9 +298,9 @@ class LoginForm extends React.PureComponent { onFocus={this.handleFocus} onBlur={this.handleBlur} onKeyDown={this.handleKeyDown} - autocomplete='off' - autocapitalize='off' - spellcheck='false' + autoComplete='off' + autoCapitalize='off' + spellCheck='false' /> <Button onClick={this.handleSubmit} disabled={isSubmitting || error}><FormattedMessage id='interaction_modal.login.action' defaultMessage='Take me home' /></Button> diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index 4b0e03a0f..d1b2a0910 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -82,7 +82,7 @@ class NavigationPanel extends Component { </div> {banner && - <div class='navigation-panel__banner'> + <div className='navigation-panel__banner'> {banner} </div> } diff --git a/app/javascript/mastodon/features/video/index.jsx b/app/javascript/mastodon/features/video/index.jsx index bef14ea27..9ff6d3589 100644 --- a/app/javascript/mastodon/features/video/index.jsx +++ b/app/javascript/mastodon/features/video/index.jsx @@ -612,7 +612,6 @@ class Video extends PureComponent { aria-label={alt} title={alt} lang={lang} - volume={volume} onClick={this.togglePlay} onKeyDown={this.handleVideoKeyDown} onPlay={this.handlePlay} diff --git a/package.json b/package.json index 543af8a4b..500d6d2cd 100644 --- a/package.json +++ b/package.json @@ -193,7 +193,7 @@ "eslint-plugin-jsx-a11y": "~6.8.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-promise": "~6.1.1", - "eslint-plugin-react": "~7.33.0", + "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "husky": "^8.0.3", "jest": "^29.5.0", diff --git a/yarn.lock b/yarn.lock index 9e1622680..774eefec8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2371,7 +2371,7 @@ __metadata: eslint-plugin-jsx-a11y: "npm:~6.8.0" eslint-plugin-prettier: "npm:^5.0.0" eslint-plugin-promise: "npm:~6.1.1" - eslint-plugin-react: "npm:~7.33.0" + eslint-plugin-react: "npm:^7.33.2" eslint-plugin-react-hooks: "npm:^4.6.0" file-loader: "npm:^6.2.0" font-awesome: "npm:^4.7.0" @@ -7501,7 +7501,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:~7.33.0": +"eslint-plugin-react@npm:^7.33.2": version: 7.33.2 resolution: "eslint-plugin-react@npm:7.33.2" dependencies: From 23b16aaab0f475d8584e948d2f4ce6d64a68cac3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:50:59 +0100 Subject: [PATCH 29/73] Update dependency selenium-webdriver to v4.16.0 (#28246) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 738562116..cd04f2782 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -707,7 +707,7 @@ GEM scenic (1.7.0) activerecord (>= 4.0.0) railties (>= 4.0.0) - selenium-webdriver (4.15.0) + selenium-webdriver (4.16.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) From af66d3d836b89286423d4ba9fe1518bb6317b499 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Wed, 6 Dec 2023 09:15:54 -0500 Subject: [PATCH 30/73] Use `following` and `followers` scopes in CLI (#28154) --- lib/mastodon/cli/accounts.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/mastodon/cli/accounts.rb b/lib/mastodon/cli/accounts.rb index 33520df25..414675303 100644 --- a/lib/mastodon/cli/accounts.rb +++ b/lib/mastodon/cli/accounts.rb @@ -472,15 +472,13 @@ module Mastodon::CLI end total = 0 - total += Account.where(id: ::Follow.where(account: account).select(:target_account_id)).count if options[:follows] - total += Account.where(id: ::Follow.where(target_account: account).select(:account_id)).count if options[:followers] + total += account.following.reorder(nil).count if options[:follows] + total += account.followers.reorder(nil).count if options[:followers] progress = create_progress_bar(total) processed = 0 if options[:follows] - scope = Account.where(id: ::Follow.where(account: account).select(:target_account_id)) - - scope.find_each do |target_account| + account.following.reorder(nil).find_each do |target_account| UnfollowService.new.call(account, target_account) rescue => e progress.log pastel.red("Error processing #{target_account.id}: #{e}") @@ -493,9 +491,7 @@ module Mastodon::CLI end if options[:followers] - scope = Account.where(id: ::Follow.where(target_account: account).select(:account_id)) - - scope.find_each do |target_account| + account.followers.reorder(nil).find_each do |target_account| UnfollowService.new.call(target_account, account) rescue => e progress.log pastel.red("Error processing #{target_account.id}: #{e}") From 658ad7a6cabe0c8b6b3ae11db1a4cc8434152ec4 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Wed, 6 Dec 2023 23:22:25 +0100 Subject: [PATCH 31/73] Fix flaky tests related to file creation (#28248) --- .../post_deployment_migration_generator_spec.rb | 2 +- spec/lib/mastodon/cli/emoji_spec.rb | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/spec/generators/post_deployment_migration_generator_spec.rb b/spec/generators/post_deployment_migration_generator_spec.rb index d770a78e9..55e70a791 100644 --- a/spec/generators/post_deployment_migration_generator_spec.rb +++ b/spec/generators/post_deployment_migration_generator_spec.rb @@ -12,7 +12,7 @@ describe PostDeploymentMigrationGenerator, type: :generator do include FileUtils tests described_class - destination File.expand_path('../../tmp', __dir__) + destination Rails.root.join('tmp', 'generator-test') before { prepare_destination } after { rm_rf(destination_root) } diff --git a/spec/lib/mastodon/cli/emoji_spec.rb b/spec/lib/mastodon/cli/emoji_spec.rb index 530da91e7..3441413b9 100644 --- a/spec/lib/mastodon/cli/emoji_spec.rb +++ b/spec/lib/mastodon/cli/emoji_spec.rb @@ -41,11 +41,17 @@ describe Mastodon::CLI::Emoji do describe '#export' do context 'with existing custom emoji' do - before { Fabricate(:custom_emoji) } - after { File.delete(export_path) } + before do + FileUtils.rm_rf(export_path.dirname) + FileUtils.mkdir_p(export_path.dirname) - let(:export_path) { Rails.root.join('tmp', 'export.tar.gz') } - let(:args) { [Rails.root.join('tmp')] } + Fabricate(:custom_emoji) + end + + after { FileUtils.rm_rf(export_path.dirname) } + + let(:export_path) { Rails.root.join('tmp', 'cli-tests', 'export.tar.gz') } + let(:args) { [export_path.dirname.to_s] } let(:action) { :export } it 'reports about exported emoji' do From 5e8a38e2540e4c067fe8756bb78a1cc854943db6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:42:47 +0100 Subject: [PATCH 32/73] Update dependency oj to v3.16.2 (#28263) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cd04f2782..45ad60558 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -484,7 +484,8 @@ GEM nokogiri (1.15.5) mini_portile2 (~> 2.8.2) racc (~> 1.4) - oj (3.16.1) + oj (3.16.2) + bigdecimal (~> 3.1) omniauth (2.1.1) hashie (>= 3.4.6) rack (>= 2.2.3) From 54db2006a96639a51d42353bef8c18b5f55b1c3c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 08:44:16 +0000 Subject: [PATCH 33/73] Update dependency sass-loader to v10.5.0 (#28254) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 774eefec8..45f58eaad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14663,8 +14663,8 @@ __metadata: linkType: hard "sass-loader@npm:^10.2.0": - version: 10.4.1 - resolution: "sass-loader@npm:10.4.1" + version: 10.5.0 + resolution: "sass-loader@npm:10.5.0" dependencies: klona: "npm:^2.0.4" loader-utils: "npm:^2.0.0" @@ -14673,7 +14673,7 @@ __metadata: semver: "npm:^7.3.2" peerDependencies: fibers: ">= 3.1.0" - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 sass: ^1.3.0 webpack: ^4.36.0 || ^5.0.0 peerDependenciesMeta: @@ -14683,7 +14683,7 @@ __metadata: optional: true sass: optional: true - checksum: bf04a440fe471928f3cf884bc12c6b70bc391795b35510b1b9021e8a2cca3b8f966aef9518f4171e87e9cb78193a774f695921e6b61881a1580ae0a3c7b1b5e4 + checksum: be5da7784fd21c4f526cc3afaa1a765ba44cdc2f9798ecbac87b296ab44184ac5ba9bbda68a7a86f8cdcb6130acceefeb8912260fca14bdfc97f9dad02658400 languageName: node linkType: hard From 336d6260ba8ace929bfb5482cc72740d013dd1a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:44:45 +0100 Subject: [PATCH 34/73] Update dependency chewy to v7.3.5 (#28253) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 45ad60558..7a7fdb01c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -197,7 +197,7 @@ GEM activesupport cbor (0.5.9.6) charlock_holmes (0.7.7) - chewy (7.3.4) + chewy (7.3.5) activesupport (>= 5.2) elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch-dsl From 7593465c23d4b0c4d44304712f7957aa1d9ce9d7 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Thu, 7 Dec 2023 10:05:08 +0100 Subject: [PATCH 35/73] Fix error when processing link preview with an array as `inLanguage` (#28252) --- app/lib/link_details_extractor.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb index a96612cab..bb031986d 100644 --- a/app/lib/link_details_extractor.rb +++ b/app/lib/link_details_extractor.rb @@ -37,6 +37,7 @@ class LinkDetailsExtractor def language lang = json['inLanguage'] + lang = lang.first if lang.is_a?(Array) lang.is_a?(Hash) ? (lang['alternateName'] || lang['name']) : lang end From e0dacf6b4ce090cf6a30f8b1f37283b174b32c40 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:38:44 +0100 Subject: [PATCH 36/73] New Crowdin Translations (automated) (#28264) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/hu.json | 54 ++++++++++++------------- config/locales/activerecord.hu.yml | 2 +- config/locales/devise.hu.yml | 8 ++-- config/locales/hu.yml | 28 ++++++------- config/locales/simple_form.hu.yml | 8 ++-- config/locales/simple_form.hy.yml | 2 +- 6 files changed, 51 insertions(+), 51 deletions(-) diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 9bef81417..d890e4188 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -13,7 +13,7 @@ "about.rules": "Kiszolgáló szabályai", "account.account_note_header": "Feljegyzés", "account.add_or_remove_from_list": "Hozzáadás vagy eltávolítás a listákról", - "account.badges.bot": "Bot", + "account.badges.bot": "Automatizált", "account.badges.group": "Csoport", "account.block": "@{name} letiltása", "account.block_domain": "Domain blokkolása: {domain}", @@ -63,8 +63,8 @@ "account.share": "@{name} profiljának megosztása", "account.show_reblogs": "@{name} megtolásainak mutatása", "account.statuses_counter": "{count, plural, one {{counter} Bejegyzés} other {{counter} Bejegyzés}}", - "account.unblock": "@{name} tiltásának feloldása", - "account.unblock_domain": "{domain} domain tiltás feloldása", + "account.unblock": "@{name} letiltásának feloldása", + "account.unblock_domain": "{domain} domain tiltásának feloldása", "account.unblock_short": "Tiltás feloldása", "account.unendorse": "Ne jelenjen meg a profilodon", "account.unfollow": "Követés megszüntetése", @@ -138,21 +138,21 @@ "compose.language.search": "Nyelv keresése...", "compose.published.body": "A bejegyzés publikálásra került.", "compose.published.open": "Megnyitás", - "compose.saved.body": "A bejegyzés mentésre került.", + "compose.saved.body": "A bejegyzés mentve.", "compose_form.direct_message_warning_learn_more": "Tudj meg többet", - "compose_form.encryption_warning": "A bejegyzések Mastodonon nem használnak végpontok közötti titkosítást. Ne ossz meg semmilyen érzékeny információt Mastodonon.", + "compose_form.encryption_warning": "A bejegyzések a Mastodonon nem használnak végpontok közti titkosítást. Ne ossz meg semmilyen érzékeny információt a Mastodonon.", "compose_form.hashtag_warning": "Ez a bejegyzésed nem fog megjelenni semmilyen hashtag alatt, mivel nem nyilvános. Csak a nyilvános bejegyzések kereshetők hashtaggel.", "compose_form.lock_disclaimer": "A fiókod nincs {locked}. Bárki követni tud, hogy megtekintse a kizárólag követőknek szánt bejegyzéseket.", - "compose_form.lock_disclaimer.lock": "lezárva", + "compose_form.lock_disclaimer.lock": "zárolva", "compose_form.placeholder": "Mi jár a fejedben?", "compose_form.poll.add_option": "Lehetőség hozzáadása", "compose_form.poll.duration": "Szavazás időtartama", "compose_form.poll.option_placeholder": "{number}. lehetőség", - "compose_form.poll.remove_option": "Lehetőség törlése", + "compose_form.poll.remove_option": "Lehetőség eltávolítása", "compose_form.poll.switch_to_multiple": "Szavazás megváltoztatása több választásosra", "compose_form.poll.switch_to_single": "Szavazás megváltoztatása egyetlen választásosra", "compose_form.publish": "Közzététel", - "compose_form.publish_form": "Közzététel", + "compose_form.publish_form": "Új bejegyzés", "compose_form.publish_loud": "{publish}!", "compose_form.save_changes": "Módosítások mentése", "compose_form.sensitive.hide": "{count, plural, one {Média kényesnek jelölése} other {Média kényesnek jelölése}}", @@ -195,13 +195,13 @@ "copy_icon_button.copied": "A szöveg a vágólapra másolva", "copypaste.copied": "Másolva", "copypaste.copy_to_clipboard": "Másolás vágólapra", - "directory.federated": "Az ismert fediverzumból", + "directory.federated": "Az ismert födiverzumból", "directory.local": "Csak {domain} tartományból", "directory.new_arrivals": "Új csatlakozók", "directory.recently_active": "Nemrég aktív", "disabled_account_banner.account_settings": "Fiókbeállítások", "disabled_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva.", - "dismissable_banner.community_timeline": "Ezek a legfrissebb nyilvános bejegyzések, amelyeket {domain} tartományban levő kiszolgáló fiókjait használó emberek tettek közzé.", + "dismissable_banner.community_timeline": "Ezek a legfrissebb nyilvános bejegyzések, amelyeket a(z) {domain} kiszolgáló fiókjait használó emberek tették közzé.", "dismissable_banner.dismiss": "Elvetés", "dismissable_banner.explore_links": "Jelenleg ezekről a hírekről beszélgetnek az ezen és a központosítás nélküli hálózat többi kiszolgálóján lévő emberek.", "dismissable_banner.explore_statuses": "Ezek jelenleg népszerűvé váló bejegyzések a háló különböző szegleteiből. Az újabb vagy több megtolással rendelkező bejegyzéseket, illetve a kedvencnek jelöléssel rendelkezőeket rangsoroljuk előrébb.", @@ -216,14 +216,14 @@ "emoji_button.food": "Étel és Ital", "emoji_button.label": "Emodzsi beszúrása", "emoji_button.nature": "Természet", - "emoji_button.not_found": "Nincsenek emodzsik!! (╯°□°)╯︵ ┻━┻", + "emoji_button.not_found": "Nem találhatók emodzsik", "emoji_button.objects": "Tárgyak", "emoji_button.people": "Emberek", "emoji_button.recent": "Gyakran használt", "emoji_button.search": "Keresés...", "emoji_button.search_results": "Keresési találatok", "emoji_button.symbols": "Szimbólumok", - "emoji_button.travel": "Utazás és Helyek", + "emoji_button.travel": "Utazás és helyek", "empty_column.account_hides_collections": "Ez a felhasználó úgy döntött, hogy nem teszi elérhetővé ezt az információt.", "empty_column.account_suspended": "Fiók felfüggesztve", "empty_column.account_timeline": "Itt nincs bejegyzés!", @@ -245,7 +245,7 @@ "empty_column.mutes": "Még egy felhasználót sem némítottál le.", "empty_column.notifications": "Jelenleg még nincsenek értesítéseid. Ha mások kapcsolatba lépnek veled, ezek itt lesznek láthatóak.", "empty_column.public": "Jelenleg itt nincs semmi! Írj valamit nyilvánosan vagy kövess más kiszolgálón levő felhasználókat, hogy megtöltsd.", - "error.unexpected_crash.explanation": "Egy hiba vagy böngésző inkompatibilitás miatt ez az oldal nem jeleníthető meg rendesen.", + "error.unexpected_crash.explanation": "Egy kód- vagy böngészőkompatibilitási hiba miatt ez az oldal nem jeleníthető meg helyesen.", "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző kiegészítő vagy egy automatikus fordító okozza.", "error.unexpected_crash.next_steps": "Próbáld frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.", "error.unexpected_crash.next_steps_addons": "Próbáld letiltani őket és frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.", @@ -278,13 +278,13 @@ "firehose.remote": "Egyéb kiszolgálók", "follow_request.authorize": "Hitelesítés", "follow_request.reject": "Elutasítás", - "follow_requests.unlocked_explanation": "Bár a fiókod nincs zárolva, a(z) {domain} csapata úgy gondolta, hogy talán kézzel szeretnéd ellenőrizni a fiók követési kéréseit.", + "follow_requests.unlocked_explanation": "Bár a fiókod nincs zárolva, a(z) {domain} csapata úgy gondolta, hogy talán kézzel szeretnéd ellenőrizni ezen fiókok követési kéréseit.", "followed_tags": "Követett hashtagek", "footer.about": "Névjegy", "footer.directory": "Profiltár", "footer.get_app": "Alkalmazás beszerzése", "footer.invite": "Emberek meghívása", - "footer.keyboard_shortcuts": "Billentyűparancsok", + "footer.keyboard_shortcuts": "Gyorsbillentyűk", "footer.privacy_policy": "Adatvédelmi szabályzat", "footer.source_code": "Forráskód megtekintése", "footer.status": "Állapot", @@ -347,13 +347,13 @@ "keyboard_shortcuts.favourite": "Bejegyzés kedvencnek jelölése", "keyboard_shortcuts.favourites": "Kedvencek lista megnyitása", "keyboard_shortcuts.federated": "Föderációs idővonal megnyitása", - "keyboard_shortcuts.heading": "Billentyűparancsok", + "keyboard_shortcuts.heading": "Gyorsbillentyűk", "keyboard_shortcuts.home": "Saját idővonal megnyitása", "keyboard_shortcuts.hotkey": "Gyorsbillentyű", "keyboard_shortcuts.legend": "jelmagyarázat megjelenítése", - "keyboard_shortcuts.local": "helyi idővonal megnyitása", + "keyboard_shortcuts.local": "Helyi idővonal megnyitása", "keyboard_shortcuts.mention": "Szerző megemlítése", - "keyboard_shortcuts.muted": "némított felhasználók listájának megnyitása", + "keyboard_shortcuts.muted": "Némított felhasználók listájának megnyitása", "keyboard_shortcuts.my_profile": "Saját profil megnyitása", "keyboard_shortcuts.notifications": "Értesítések oszlop megnyitása", "keyboard_shortcuts.open_media": "Média megnyitása", @@ -389,7 +389,7 @@ "lists.replies_policy.list": "A lista tagjai", "lists.replies_policy.none": "Senki", "lists.replies_policy.title": "Nekik mutassuk a válaszokat:", - "lists.search": "Keresés a követett személyek között", + "lists.search": "Keresés a követett emberek között", "lists.subheading": "Saját listák", "load_pending": "{count, plural, one {# új elem} other {# új elem}}", "loading_indicator.label": "Betöltés…", @@ -399,7 +399,7 @@ "mute_modal.hide_notifications": "Rejtsük el a felhasználótól származó értesítéseket?", "mute_modal.indefinite": "Határozatlan", "navigation_bar.about": "Névjegy", - "navigation_bar.advanced_interface": "Haladó webes felület engedélyezése", + "navigation_bar.advanced_interface": "Megnyitás a speciális webes felületben", "navigation_bar.blocks": "Letiltott felhasználók", "navigation_bar.bookmarks": "Könyvjelzők", "navigation_bar.community_timeline": "Helyi idővonal", @@ -411,9 +411,9 @@ "navigation_bar.explore": "Felfedezés", "navigation_bar.favourites": "Kedvencek", "navigation_bar.filters": "Némított szavak", - "navigation_bar.follow_requests": "Követési kérelmek", + "navigation_bar.follow_requests": "Követési kérések", "navigation_bar.followed_tags": "Követett hashtagek", - "navigation_bar.follows_and_followers": "Követettek és követők", + "navigation_bar.follows_and_followers": "Követések és követők", "navigation_bar.lists": "Listák", "navigation_bar.logout": "Kijelentkezés", "navigation_bar.mutes": "Némított felhasználók", @@ -449,7 +449,7 @@ "notifications.column_settings.follow_request": "Új követési kérelmek:", "notifications.column_settings.mention": "Megemlítések:", "notifications.column_settings.poll": "Szavazási eredmények:", - "notifications.column_settings.push": "Push értesítések", + "notifications.column_settings.push": "Leküldéses értesítések", "notifications.column_settings.reblog": "Megtolások:", "notifications.column_settings.show": "Megjelenítés az oszlopban", "notifications.column_settings.sound": "Hang lejátszása", @@ -480,7 +480,7 @@ "onboarding.compose.template": "Üdvözlet, #Mastodon!", "onboarding.follows.empty": "Sajnos jelenleg nem jeleníthető meg eredmény. Kipróbálhatod a keresést vagy böngészheted a felfedező oldalon a követni kívánt személyeket, vagy próbáld meg később.", "onboarding.follows.lead": "A saját hírfolyamod az elsődleges tapasztalás a Mastodonon. Minél több embert követsz, annál aktívabb és érdekesebb a dolog. Az induláshoz itt van néhány javaslat:", - "onboarding.follows.title": "Népszerű a Mastodonon", + "onboarding.follows.title": "Szabd személyre a kezdőlapodat", "onboarding.profile.discoverable": "Saját profil beállítása felfedezhetőként", "onboarding.profile.discoverable_hint": "A Mastodonon a felfedezhetőség választása esetén a saját bejegyzéseid megjelenhetnek a keresési eredmények és a felkapott tartalmak között, valamint a profilod a hozzád hasonló érdeklődési körrel rendelkező embereknél is ajánlásra kerülhet.", "onboarding.profile.display_name": "Megjelenített név", @@ -499,14 +499,14 @@ "onboarding.start.lead": "Az új Mastodon-fiók használatra kész. Így hozhatod ki belőle a legtöbbet:", "onboarding.start.skip": "Szeretnél előreugrani?", "onboarding.start.title": "Ez sikerült!", - "onboarding.steps.follow_people.body": "Te állítod össze a saját hírfolyamodat. Töltsd meg érdekes emberekkel.", + "onboarding.steps.follow_people.body": "A Mastodon az érdekes emberek követéséről szól.", "onboarding.steps.follow_people.title": "{count, plural, one {egy ember} other {# ember}} követése", "onboarding.steps.publish_status.body": "Üdvözöljük a világot.", "onboarding.steps.publish_status.title": "Az első bejegyzés létrehozása", "onboarding.steps.setup_profile.body": "Mások nagyobb valószínűséggel lépnek kapcsolatba veled egy kitöltött profil esetén.", "onboarding.steps.setup_profile.title": "Profilod testreszabása", - "onboarding.steps.share_profile.body": "Tudasd az ismerőseiddel, hogyan találhatnak meg a Mastodonon!", - "onboarding.steps.share_profile.title": "Profilod megosztása", + "onboarding.steps.share_profile.body": "Tudasd az ismerőseiddel, hogyan találhatnak meg a Mastodonon", + "onboarding.steps.share_profile.title": "Oszd meg a Mastodon profilodat", "onboarding.tips.2fa": "<strong>Tudtad?</strong> A fiókod biztonságossá teheted, ha a fiók beállításaiban beállítod a kétlépcsős hitelesítést. Bármilyen választott TOTP alkalmazással működik, nincs szükség telefonszámra!", "onboarding.tips.accounts_from_other_servers": "<strong>Tudtad?</strong> Mivel a Mastodon decentralizált, egyes profilok, amelyekkel találkozol, más kiszolgálókon lesznek tárolva. És mégis zökkenőmentesen kommunikálhatsz velük! A kiszolgáló a felhasználónevük második felében található!", "onboarding.tips.migration": "<strong>Tudtad?</strong> Ha úgy érzed, hogy a {domain} már nem jó kiszolgáló a számodra, átköltözhetsz egy másik Mastodon kiszolgálóra anélkül, hogy elveszítenéd a követőidet. Akár saját kiszolgálót is üzemeltethetsz!", diff --git a/config/locales/activerecord.hu.yml b/config/locales/activerecord.hu.yml index e5757ba64..f34ade044 100644 --- a/config/locales/activerecord.hu.yml +++ b/config/locales/activerecord.hu.yml @@ -20,7 +20,7 @@ hu: attributes: username: invalid: csak betűket, számokat vagy alávonást tartalmazhat - reserved: fenntartott + reserved: foglalt admin/webhook: attributes: url: diff --git a/config/locales/devise.hu.yml b/config/locales/devise.hu.yml index bbf6a6399..6eb1524ed 100644 --- a/config/locales/devise.hu.yml +++ b/config/locales/devise.hu.yml @@ -44,7 +44,7 @@ hu: action: Jelszó módosítása explanation: A fiókodhoz tartozó jelszó módosítását kezdeményezted. extra: Amennyiben nem te kezdeményezted a módosítást, kérjük tekintsd ezt az emailt tárgytalannak. A jelszavad változatlan marad mindaddig, amíg újat nem hozol létre a fenti linkre kattintva. - subject: 'Mastodon: Jelszó visszaállítási lépések' + subject: 'Mastodon: Jelszóvisszaállítási utasítások' title: Jelszó visszaállítása two_factor_disabled: explanation: A fiókod kétlépcsős hitelesítését kikapcsoltuk. A bejelentkezés mostantól csak az e-mail cím és a jelszó használatával lesz lehetséges. @@ -52,14 +52,14 @@ hu: title: Kétlépcsős hitelesítés kikapcsolva two_factor_enabled: explanation: A kétlépcsős hitelesítést engedélyeztük a fiókodban. A bejelentkezéshez a párosított TOTP alkalmazás által generált tokenre lesz szükség. - subject: Kétlépcsős azonosítás engedélyezve + subject: 'Mastodon: Kétlépcsős azonosítás engedélyezve' title: Kétlépcsős hitelesítés engedélyezve two_factor_recovery_codes_changed: explanation: A korábbi helyreállítási kódok letiltásra és újragenerálásra kerültek. - subject: Kétlépcsős helyreállítási kódok újra létrejöttek + subject: 'Mastodon: Kétlépcsős helyreállítási kódok újból előállítva' title: A kétlépcsős kódok megváltoztak unlock_instructions: - subject: 'Mastodon: Feloldási lépések' + subject: 'Mastodon: Feloldási utasítások' webauthn_credential: added: explanation: A következő biztonsági kulcsot hozzáadtuk a fiókodhoz diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 7057883e1..5da1a4e06 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -4,7 +4,7 @@ hu: about_mastodon_html: 'A jövő közösségi hálózata: Hirdetések és céges megfigyelés nélkül, etikus dizájnnal és decentralizációval! Legyél a saját adataid ura a Mastodonnal!' contact_missing: Nincs megadva contact_unavailable: N/A - hosted_on: "%{domain} Mastodon szerver" + hosted_on: "%{domain} Mastodon-kiszolgáló" title: Névjegy accounts: follow: Követés @@ -747,7 +747,7 @@ hu: desc_html: Ez hCaptcha-ból származó külső scripteket használ, mely biztonsági vagy adatvédelmi résnek bizonyulhat. Ezen kívül ez <strong>a regisztrációs folyamatot jelentősen megnehezítheti bizonyos (kifejezetten különleges szükségletű) emberek számára</strong>. Emiatt fontold meg más módszerek, mint pl. jóváhagyás-alapú vagy meghívásalapú regisztráció használatát. title: Az új felhasználóknak egy CAPTCHA-t kell megoldaniuk, hogy megerősítsék a fiókjuk regisztrációját content_retention: - preamble: Felhasználók által generált tartalom Mastodonon való tárolásának szabályozása. + preamble: A felhasználók által előállított tartalom Mastodonon való tárolásának szabályozása. title: Tartalom megtartása default_noindex: desc_html: Azokat a felhasználókat érinti, akik nem módosították ezt a beállítást @@ -1007,8 +1007,8 @@ hu: hint_html: Ha másik fiókról kívánsz átlépni erre a fiókra, itt létrehozhatsz egy aliast, amelyre szükség van, mielőtt folytathatod a követők áthelyezését a régi fiókból erre. Ez az áthelyezés önmagában <strong>ártalmatlan és visszafordítható</strong> folyamat. <strong>A fiók áttelepítése a régi fiókból indul el. </strong> remove: Alias szétkapcsolása appearance: - advanced_web_interface: Haladó webes felület - advanced_web_interface_hint: 'Ha szeretnéd, a teljes képernyőszélességet felhasználhatod. A haladó webes felülettel különböző oszlopokat állíthatsz be, hogy egyszerre annyi infót láthass, amennyit csak akarsz: Saját idővonal, értesítések, föderációs idővonal, bármennyi lista vagy hashtag.' + advanced_web_interface: Speciális webes felület + advanced_web_interface_hint: 'Ha szeretnéd, a képernyő teljes szélességét kihasználhatod. A speciális webes felülettel különböző oszlopokat állíthatsz be, hogy egyszerre annyi információt láthass, amennyit csak akarsz: Kezdőoldal, értesítések, föderációs idővonal, bármennyi lista vagy hashtag.' animations_and_accessibility: Animáció és akadálymentesítés confirmation_dialogs: Megerősítő párbeszédablakok discovery: Felfedezés @@ -1052,7 +1052,7 @@ hu: delete_account: Felhasználói fiók törlése delete_account_html: Felhasználói fiókod törléséhez <a href="%{path}">kattints ide</a>. A rendszer újbóli megerősítést fog kérni. description: - prefix_invited_by_user: "@%{name} meghív téged, hogy csatlakozz ehhez a Mastodon kiszolgálóhoz." + prefix_invited_by_user: "@%{name} meghív téged, hogy csatlakozz ehhez a Mastodon-kiszolgálóhoz." prefix_sign_up: Regisztrláj még ma a Mastodonra! suffix: Egy fiókkal követhetsz másokat, bejegyzéseket tehetsz közzé, eszmét cserélhetsz más Mastodon szerverek felhasználóival! didnt_get_confirmation: Nem kaptál visszaigazoló hivatkozást? @@ -1101,7 +1101,7 @@ hu: title: 'Bejelentkezés ide: %{domain}' sign_up: manual_review: A(z) %{domain} regisztrációi a moderátorok kézi felülvizsgálatán mennek át. Hogy segítsd a regisztráció feldolgozását, írj röviden magadról, és hogy miért szeretnél fiókot a(z) %{domain} oldalon. - preamble: Egy fiókkal ezen a Mastodon kiszolgálón követhetsz bárkit a hálózaton, függetlenül attól, hogy az illető fiókja melyik kiszolgálón található. + preamble: Egy fiókkal ezen a Mastodon-kiszolgálón követhetsz bárkit a hálózaton, függetlenül attól, hogy az illető fiókja melyik kiszolgálón található. title: Állítsuk be a fiókod a %{domain} kiszolgálón. status: account_status: Fiók állapota @@ -1234,7 +1234,7 @@ hu: filters: contexts: account: Profil - home: Saját idővonal + home: Kezdőlap és listák notifications: Értesítések public: Nyilvános idővonalak thread: Beszélgetések @@ -1245,7 +1245,7 @@ hu: statuses_hint_html: Ez a szűrő egyedi bejegyzések kiválasztására vonatkozik, függetlenül attól, hogy megfelelnek-e a lenti kulcsszavaknak. <a href="%{path}">Engedélyezze vagy távolítsa el a bejegyzéseket a szűrőből</a>. title: Szűrő szerkesztése errors: - deprecated_api_multiple_keywords: Ezek a paraméterek nem módosíthatóak az alkalmazásból, mert több mint egy szűrőkulcsszóra is hatással vannak. Használd az alkalmazás vagy a webes felület újabb verzióját. + deprecated_api_multiple_keywords: Ezek a paraméterek nem módosíthatók az alkalmazásból, mert egynél több szűrőkulcsszóra is hatással vannak. Használd az alkalmazás vagy a webes felület újabb verzióját. invalid_context: A megadott kontextus hamis vagy hiányzik index: contexts: 'Szűrés helye: %{contexts}' @@ -1396,7 +1396,7 @@ hu: unsubscribe: action: Igen, leiratkozás complete: Leiratkozva - confirmation_html: Biztos vagy benne, hogy le szeretnél iratkozni arról, hogy %{type} típusú üzeneteket kapj a %{domain} Mastodon kiszolgálón a %{email} címedre? Bármikor újra feliratkozhatsz az <a href="%{settings_path}">email értesítések beállításainál</a>. + confirmation_html: 'Biztos, hogy leiratkozol arról, hogy %{type} típusú üzeneteket kapj a %{domain} Mastodon-kiszolgálótól erre a címedre: %{email}? Bármikor újra feliratkozhatsz az <a href="%{settings_path}">e-mail-értesítések beállításánál</a>.' emails: notification_emails: favourite: kedvencnek jelölésről email értesítő @@ -1405,7 +1405,7 @@ hu: mention: megemlítésről email értesítő reblog: megtolásról email értesítő resubscribe_html: Ha tévedésből iratkoztál le, újra feliratkozhatsz az <a href="%{settings_path}">email értesítések beállításainál</a>. - success_html: Mostantól nem kapsz %{type} típusú üzeneket a %{domain} Mastodon kiszolgálón a %{email} címedre. + success_html: 'Mostantól nem kapsz %{type} típusú üzeneket a(z) %{domain} Mastodon-kiszolgálón erre a címedre: %{email}.' title: Leiratkozás media_attachments: validations: @@ -1743,9 +1743,9 @@ hu: tags: does_not_match_previous_name: nem illeszkedik az előző névvel themes: - contrast: Mastodon (Nagy kontrasztú) - default: Mastodon (Sötét) - mastodon-light: Mastodon (Világos) + contrast: Mastodon (nagy kontrasztú) + default: Mastodon (sötét) + mastodon-light: Mastodon (világos) time: formats: default: "%Y. %b %d., %H:%M" @@ -1782,7 +1782,7 @@ hu: subject: A %{date}-i fellebbezésedet visszautasították title: Fellebbezés visszautasítva backup_ready: - explanation: A Mastodon fiókod teljes mentését kérted. A mentés kész ás letölthető! + explanation: A Mastodon-fiókod teljes mentését kérted. A mentés elkészült, és letölthető. subject: Az adataidról készült archív letöltésre kész title: Archiválás suspicious_sign_in: diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml index 70c225e8f..6e11f7fb9 100644 --- a/config/locales/simple_form.hu.yml +++ b/config/locales/simple_form.hu.yml @@ -81,8 +81,8 @@ hu: bootstrap_timeline_accounts: Ezek a fiókok ki lesznek tűzve az új felhasználók követési javaslatainak élére. closed_registrations_message: Akkor jelenik meg, amikor a regisztráció le van zárva content_cache_retention_period: A más kiszolgálókról származó bejegyzések megadott számú nap után törölve lesznek, ha pozitív értékre van állítva. Ez lehet, hogy nem fordítható vissza. - custom_css: A Mastodon webes verziójában használhatsz egyedi stílusokat. - mascot: Felülvágja a haladó webes felületen található illusztrációt. + custom_css: A Mastodon webes verziójában használhatsz egyéni stílusokat. + mascot: Felülbírálja a speciális webes felületen található illusztrációt. media_cache_retention_period: A letöltött médiafájlok megadott számú nap után törölve lesznek, ha pozitív értékre van állítva, és igény szerint újból le lesznek töltve. peers_api_enabled: Azon domainek listája, melyekkel ez a kiszolgáló találkozott a fediverzumban. Nem csatolunk adatot arról, hogy föderált kapcsolatban vagy-e az adott kiszolgálóval, csak arról, hogy a kiszolgálód tud a másikról. Ezt olyan szolgáltatások használják, melyek általában a föderációról készítenek statisztikákat. profile_directory: A profilok jegyzéke minden olyan felhasználót felsorol, akik engedélyezték a felfedezhetőségüket. @@ -103,7 +103,7 @@ hu: form_challenge: current_password: Beléptél egy biztonsági térben imports: - data: Egy másik Mastodon kiszolgálóról exportált CSV-fájl + data: Egy másik Mastodon-kiszolgálóról exportált CSV-fájl invite_request: text: Ez segít nekünk átnézni a jelentkezésedet ip_block: @@ -199,7 +199,7 @@ hu: otp_attempt: Kétlépcsős azonosító kód password: Jelszó phrase: Kulcsszó vagy kifejezés - setting_advanced_layout: Haladó webes felület engedélyezése + setting_advanced_layout: Speciális webes felület engedélyezése setting_aggregate_reblogs: Megtolások csoportosítása az idővonalakon setting_always_send_emails: E-mail értesítések küldése mindig setting_auto_play_gif: GIF-ek automatikus lejátszása diff --git a/config/locales/simple_form.hy.yml b/config/locales/simple_form.hy.yml index 56aa1d66b..9dbcd1301 100644 --- a/config/locales/simple_form.hy.yml +++ b/config/locales/simple_form.hy.yml @@ -43,7 +43,7 @@ hy: setting_display_media_hide_all: Երբեք մեդիա ցոյց չտալ setting_display_media_show_all: Մեդիա միշտ ցոյց տալ setting_use_blurhash: Կտորները հիմնուում են թաքցուած վիզուալի վրայ՝ խամրեցնելով դետալները - setting_use_pending_items: Թաքցնել հոսքի թարմացումները կտտոի ետեւում՝ աւտօմատ թարմացուող հոսքի փոխարէն + setting_use_pending_items: Թաքցնել հոսքի թարմացումները կոճակի ետեւում՝ աւտօմատ թարմացուող հոսքի փոխարէն username: Միայն լատինատառեր, թուեր եւ տակի գծիկ whole_word: Եթէ բանալի բառը կամ արտայայտութիւնը պարունակում է միայն այբբենական նիշեր եւ թուեր, ապա այն կիրառուելու է ամբողջ բառի հետ համընկնելու դէպքում միայն domain_allow: From 1d7b8234c94e13ad3f00455e7a5f0702e9379228 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 7 Dec 2023 04:41:00 -0500 Subject: [PATCH 37/73] Remove useless `reorder(nil)` call in `tootctl statuses` (#28141) --- lib/mastodon/cli/statuses.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/cli/statuses.rb b/lib/mastodon/cli/statuses.rb index 0d6018a2b..7acf3f9b7 100644 --- a/lib/mastodon/cli/statuses.rb +++ b/lib/mastodon/cli/statuses.rb @@ -120,7 +120,7 @@ module Mastodon::CLI say('Beginning removal of now-orphaned media attachments to free up disk space...') - scope = MediaAttachment.reorder(nil).unattached.where('created_at < ?', options[:days].pred.days.ago) + scope = MediaAttachment.unattached.where('created_at < ?', options[:days].pred.days.ago) processed = 0 removed = 0 progress = create_progress_bar(scope.count) From 0b4a3a04378ce43f2f314b9446b5053f6b374c6d Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 7 Dec 2023 07:15:50 -0500 Subject: [PATCH 38/73] Remove remaining `without_partial_double_verification` usage (#28127) --- spec/helpers/application_helper_spec.rb | 24 +++++++++----- spec/helpers/home_helper_spec.rb | 17 +++++++--- spec/helpers/media_component_helper_spec.rb | 36 +++++---------------- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 3cc88014c..0a55770ba 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -29,15 +29,25 @@ describe ApplicationHelper do describe 'body_classes' do context 'with a body class string from a controller' do - before do - without_partial_double_verification do - allow(helper).to receive_messages(body_class_string: 'modal-layout compose-standalone', current_theme: 'default', current_account: Fabricate(:account)) - end - end + before { helper.extend controller_helpers } it 'uses the controller body classes in the result' do expect(helper.body_classes).to match(/modal-layout compose-standalone/) end + + private + + def controller_helpers + Module.new do + def body_class_string = 'modal-layout compose-standalone' + + def current_account + @current_account ||= Fabricate(:account) + end + + def current_theme = 'default' + end + end end end @@ -122,9 +132,7 @@ describe ApplicationHelper do describe 'available_sign_up_path' do context 'when registrations are closed' do before do - without_partial_double_verification do - allow(Setting).to receive(:registrations_mode).and_return('none') - end + allow(Setting).to receive(:[]).with('registrations_mode').and_return 'none' end it 'redirects to joinmastodon site' do diff --git a/spec/helpers/home_helper_spec.rb b/spec/helpers/home_helper_spec.rb index c6baec5a1..befc8a5c8 100644 --- a/spec/helpers/home_helper_spec.rb +++ b/spec/helpers/home_helper_spec.rb @@ -23,12 +23,19 @@ RSpec.describe HomeHelper do context 'with a valid account' do let(:account) { Fabricate(:account) } - it 'returns a link to the account' do - without_partial_double_verification do - allow(helper).to receive_messages(current_account: account, prefers_autoplay?: false) - result = helper.account_link_to(account) + before { helper.extend controller_helpers } - expect(result).to match "@#{account.acct}" + it 'returns a link to the account' do + result = helper.account_link_to(account) + + expect(result).to match "@#{account.acct}" + end + + private + + def controller_helpers + Module.new do + def current_account = Account.last end end end diff --git a/spec/helpers/media_component_helper_spec.rb b/spec/helpers/media_component_helper_spec.rb index 149f6a83a..af5d92769 100644 --- a/spec/helpers/media_component_helper_spec.rb +++ b/spec/helpers/media_component_helper_spec.rb @@ -3,16 +3,12 @@ require 'rails_helper' describe MediaComponentHelper do + before { helper.extend controller_helpers } + describe 'render_video_component' do let(:media) { Fabricate(:media_attachment, type: :video, status: Fabricate(:status)) } let(:result) { helper.render_video_component(media.status) } - before do - without_partial_double_verification do - allow(helper).to receive(:current_account).and_return(media.account) - end - end - it 'renders a react component for the video' do expect(parsed_html.div['data-component']).to eq('Video') end @@ -22,12 +18,6 @@ describe MediaComponentHelper do let(:media) { Fabricate(:media_attachment, type: :audio, status: Fabricate(:status)) } let(:result) { helper.render_audio_component(media.status) } - before do - without_partial_double_verification do - allow(helper).to receive(:current_account).and_return(media.account) - end - end - it 'renders a react component for the audio' do expect(parsed_html.div['data-component']).to eq('Audio') end @@ -37,12 +27,6 @@ describe MediaComponentHelper do let(:media) { Fabricate(:media_attachment, type: :audio, status: Fabricate(:status)) } let(:result) { helper.render_media_gallery_component(media.status) } - before do - without_partial_double_verification do - allow(helper).to receive(:current_account).and_return(media.account) - end - end - it 'renders a react component for the media gallery' do expect(parsed_html.div['data-component']).to eq('MediaGallery') end @@ -54,10 +38,6 @@ describe MediaComponentHelper do before do PreviewCardsStatus.create(status: status, preview_card: Fabricate(:preview_card)) - - without_partial_double_verification do - allow(helper).to receive(:current_account).and_return(status.account) - end end it 'returns the correct react component markup' do @@ -69,12 +49,6 @@ describe MediaComponentHelper do let(:status) { Fabricate(:status, poll: Fabricate(:poll)) } let(:result) { helper.render_poll_component(status) } - before do - without_partial_double_verification do - allow(helper).to receive(:current_account).and_return(status.account) - end - end - it 'returns the correct react component markup' do expect(parsed_html.div['data-component']).to eq('Poll') end @@ -85,4 +59,10 @@ describe MediaComponentHelper do def parsed_html Nokogiri::Slop(result) end + + def controller_helpers + Module.new do + def current_account = Account.last + end + end end From 5d97a897c8984d5f4fc0e2dca0799b5ab59a7c65 Mon Sep 17 00:00:00 2001 From: Ken Greeff <ken@kengreeff.com> Date: Thu, 7 Dec 2023 23:45:55 +1100 Subject: [PATCH 39/73] Add note to install libidn on MacOS (#28259) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index beab2c355..7b22d61b5 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ To set up **MacOS** for native development, complete the following steps: - Run `brew install postgresql@14` - Run `brew install redis` - Run `brew install imagemagick` +- Run `brew install libidn` - Install Foreman or a similar tool (such as [overmind](https://github.com/DarthSim/overmind)) to handle multiple process launching. - Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from .nvmrc - Run `corepack enable && corepack prepare` From ad34d33bfd10d3ff8078032e9f390c30892e80c1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 7 Dec 2023 08:49:14 -0500 Subject: [PATCH 40/73] Formalize some patterns in cli specs (#28255) --- spec/lib/mastodon/cli/accounts_spec.rb | 431 +++++++++--------- spec/lib/mastodon/cli/cache_spec.rb | 29 +- .../cli/canonical_email_blocks_spec.rb | 26 +- spec/lib/mastodon/cli/domains_spec.rb | 14 +- .../mastodon/cli/email_domain_blocks_spec.rb | 63 +-- spec/lib/mastodon/cli/emoji_spec.rb | 12 +- spec/lib/mastodon/cli/feeds_spec.rb | 29 +- spec/lib/mastodon/cli/ip_blocks_spec.rb | 113 ++--- spec/lib/mastodon/cli/main_spec.rb | 15 +- spec/lib/mastodon/cli/maintenance_spec.rb | 24 +- spec/lib/mastodon/cli/media_spec.rb | 73 ++- spec/lib/mastodon/cli/preview_cards_spec.rb | 33 +- spec/lib/mastodon/cli/settings_spec.rb | 39 +- spec/lib/mastodon/cli/statuses_spec.rb | 17 +- spec/lib/mastodon/cli/upgrade_spec.rb | 13 +- spec/rails_helper.rb | 1 + spec/support/command_line_helpers.rb | 9 + 17 files changed, 492 insertions(+), 449 deletions(-) create mode 100644 spec/support/command_line_helpers.rb diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index 3216d0d1b..06860c2ff 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -4,7 +4,11 @@ require 'rails_helper' require 'mastodon/cli/accounts' describe Mastodon::CLI::Accounts do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' @@ -27,15 +31,17 @@ describe Mastodon::CLI::Accounts do end describe '#create' do + let(:action) { :create } + shared_examples 'a new user with given email address and username' do it 'creates a new user with the specified email address' do - cli.invoke(:create, arguments, options) + subject expect(User.find_by(email: options[:email])).to be_present end it 'creates a new local account with the specified username' do - cli.invoke(:create, arguments, options) + subject expect(Account.find_local('tootctl_username')).to be_present end @@ -43,9 +49,8 @@ describe Mastodon::CLI::Accounts do it 'returns "OK" and newly generated password' do allow(SecureRandom).to receive(:hex).and_return('test_password') - expect { cli.invoke(:create, arguments, options) }.to output( - a_string_including("OK\nNew password: test_password") - ).to_stdout + expect { subject } + .to output_results("OK\nNew password: test_password") end end @@ -61,9 +66,8 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: 'invalid' } } it 'exits with an error message' do - expect { cli.invoke(:create, arguments, options) }.to output( - a_string_including('Failure/Error: email') - ).to_stdout + expect { subject } + .to output_results('Failure/Error: email') .and raise_error(SystemExit) end end @@ -75,7 +79,7 @@ describe Mastodon::CLI::Accounts do it_behaves_like 'a new user with given email address and username' it 'creates a new user with confirmed status' do - cli.invoke(:create, arguments, options) + subject user = User.find_by(email: options[:email]) @@ -93,7 +97,7 @@ describe Mastodon::CLI::Accounts do it_behaves_like 'a new user with given email address and username' it 'creates a new user with approved status' do - cli.invoke(:create, arguments, options) + subject user = User.find_by(email: options[:email]) @@ -109,7 +113,7 @@ describe Mastodon::CLI::Accounts do it_behaves_like 'a new user with given email address and username' it 'creates a new user and assigns the specified role' do - cli.invoke(:create, arguments, options) + subject role = User.find_by(email: options[:email])&.role @@ -121,9 +125,8 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: 'tootctl@example.com', role: '404' } } it 'exits with an error message indicating the role name was not found' do - expect { cli.invoke(:create, arguments, options) }.to output( - a_string_including('Cannot find user role with that name') - ).to_stdout + expect { subject } + .to output_results('Cannot find user role with that name') .and raise_error(SystemExit) end end @@ -139,16 +142,15 @@ describe Mastodon::CLI::Accounts do end it 'returns an error message indicating the username is already taken' do - expect { cli.invoke(:create, arguments, options) }.to output( - a_string_including("The chosen username is currently in use\nUse --force to reattach it anyway and delete the other user") - ).to_stdout + expect { subject } + .to output_results("The chosen username is currently in use\nUse --force to reattach it anyway and delete the other user") end context 'with --force option' do let(:options) { { email: 'tootctl_new@example.com', reattach: true, force: true } } it 'reattaches the account to the new user and deletes the previous user' do - cli.invoke(:create, arguments, options) + subject user = Account.find_local('tootctl_username')&.user @@ -173,20 +175,21 @@ describe Mastodon::CLI::Accounts do let(:arguments) { ['tootctl_username'] } it 'raises a required argument missing error (Thor::RequiredArgumentMissingError)' do - expect { cli.invoke(:create, arguments) } + expect { subject } .to raise_error(Thor::RequiredArgumentMissingError) end end end describe '#modify' do + let(:action) { :modify } + context 'when the given username is not found' do let(:arguments) { ['non_existent_username'] } it 'exits with an error message indicating the user was not found' do - expect { cli.invoke(:modify, arguments) }.to output( - a_string_including('No user with such username') - ).to_stdout + expect { subject } + .to output_results('No user with such username') .and raise_error(SystemExit) end end @@ -197,13 +200,12 @@ describe Mastodon::CLI::Accounts do context 'when no option is provided' do it 'returns a successful message' do - expect { cli.invoke(:modify, arguments) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') end it 'does not modify the user' do - cli.invoke(:modify, arguments) + subject expect(user).to eq(user.reload) end @@ -214,9 +216,8 @@ describe Mastodon::CLI::Accounts do let(:options) { { role: '404' } } it 'exits with an error message indicating the role was not found' do - expect { cli.invoke(:modify, arguments, options) }.to output( - a_string_including('Cannot find user role with that name') - ).to_stdout + expect { subject } + .to output_results('Cannot find user role with that name') .and raise_error(SystemExit) end end @@ -226,7 +227,7 @@ describe Mastodon::CLI::Accounts do let(:options) { { role: default_role.name } } it "updates the user's role to the specified role" do - cli.invoke(:modify, arguments, options) + subject role = user.reload.role @@ -241,7 +242,7 @@ describe Mastodon::CLI::Accounts do let(:user) { Fabricate(:user, role: role) } it "removes the user's role successfully" do - cli.invoke(:modify, arguments, options) + subject role = user.reload.role @@ -254,13 +255,13 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: 'new_email@email.com' } } it "sets the user's unconfirmed email to the provided email address" do - cli.invoke(:modify, arguments, options) + subject expect(user.reload.unconfirmed_email).to eq(options[:email]) end it "does not update the user's original email address" do - cli.invoke(:modify, arguments, options) + subject expect(user.reload.email).to eq('old_email@email.com') end @@ -270,13 +271,13 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: 'new_email@email.com', confirm: true } } it "updates the user's email address to the provided email" do - cli.invoke(:modify, arguments, options) + subject expect(user.reload.email).to eq(options[:email]) end it "sets the user's email address as confirmed" do - cli.invoke(:modify, arguments, options) + subject expect(user.reload.confirmed?).to be(true) end @@ -288,7 +289,7 @@ describe Mastodon::CLI::Accounts do let(:options) { { confirm: true } } it "confirms the user's email address" do - cli.invoke(:modify, arguments, options) + subject expect(user.reload.confirmed?).to be(true) end @@ -303,7 +304,7 @@ describe Mastodon::CLI::Accounts do end it 'approves the user' do - expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.approved }.from(false).to(true) + expect { subject }.to change { user.reload.approved }.from(false).to(true) end end @@ -312,7 +313,7 @@ describe Mastodon::CLI::Accounts do let(:options) { { disable: true } } it 'disables the user' do - expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.disabled }.from(false).to(true) + expect { subject }.to change { user.reload.disabled }.from(false).to(true) end end @@ -321,7 +322,7 @@ describe Mastodon::CLI::Accounts do let(:options) { { enable: true } } it 'enables the user' do - expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.disabled }.from(true).to(false) + expect { subject }.to change { user.reload.disabled }.from(true).to(false) end end @@ -331,9 +332,8 @@ describe Mastodon::CLI::Accounts do it 'returns a new password for the user' do allow(SecureRandom).to receive(:hex).and_return('new_password') - expect { cli.invoke(:modify, arguments, options) }.to output( - a_string_including('new_password') - ).to_stdout + expect { subject } + .to output_results('new_password') end end @@ -342,7 +342,7 @@ describe Mastodon::CLI::Accounts do let(:options) { { disable_2fa: true } } it 'disables the two-factor authentication for the user' do - expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.otp_required_for_login }.from(true).to(false) + expect { subject }.to change { user.reload.otp_required_for_login }.from(true).to(false) end end @@ -351,9 +351,8 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: 'invalid' } } it 'exits with an error message' do - expect { cli.invoke(:modify, arguments, options) }.to output( - a_string_including('Failure/Error: email') - ).to_stdout + expect { subject } + .to output_results('Failure/Error: email') .and raise_error(SystemExit) end end @@ -361,9 +360,8 @@ describe Mastodon::CLI::Accounts do end describe '#delete' do + let(:action) { :delete } let(:account) { Fabricate(:account) } - let(:arguments) { [account.username] } - let(:options) { { email: account.user.email } } let(:delete_account_service) { instance_double(DeleteAccountService) } before do @@ -372,26 +370,29 @@ describe Mastodon::CLI::Accounts do end context 'when both username and --email are provided' do + let(:arguments) { [account.username] } + let(:options) { { email: account.user.email } } + it 'exits with an error message indicating that only one should be used' do - expect { cli.invoke(:delete, arguments, options) }.to output( - a_string_including('Use username or --email, not both') - ).to_stdout + expect { subject } + .to output_results('Use username or --email, not both') .and raise_error(SystemExit) end end context 'when neither username nor --email are provided' do it 'exits with an error message indicating that no username was provided' do - expect { cli.invoke(:delete) }.to output( - a_string_including('No username provided') - ).to_stdout + expect { subject } + .to output_results('No username provided') .and raise_error(SystemExit) end end context 'when username is provided' do + let(:arguments) { [account.username] } + it 'deletes the specified user successfully' do - cli.invoke(:delete, arguments) + subject expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once end @@ -400,15 +401,14 @@ describe Mastodon::CLI::Accounts do let(:options) { { dry_run: true } } it 'does not delete the specified user' do - cli.invoke(:delete, arguments, options) + subject expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false) end it 'outputs a successful message in dry run mode' do - expect { cli.invoke(:delete, arguments, options) }.to output( - a_string_including('OK (DRY RUN)') - ).to_stdout + expect { subject } + .to output_results('OK (DRY RUN)') end end @@ -416,17 +416,18 @@ describe Mastodon::CLI::Accounts do let(:arguments) { ['non_existent_username'] } it 'exits with an error message indicating that no user was found' do - expect { cli.invoke(:delete, arguments) }.to output( - a_string_including('No user with such username') - ).to_stdout + expect { subject } + .to output_results('No user with such username') .and raise_error(SystemExit) end end end context 'when --email is provided' do + let(:options) { { email: account.user.email } } + it 'deletes the specified user successfully' do - cli.invoke(:delete, nil, options) + subject expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once end @@ -435,15 +436,14 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: account.user.email, dry_run: true } } it 'does not delete the user' do - cli.invoke(:delete, nil, options) + subject expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false) end it 'outputs a successful message in dry run mode' do - expect { cli.invoke(:delete, nil, options) }.to output( - a_string_including('OK (DRY RUN)') - ).to_stdout + expect { subject } + .to output_results('OK (DRY RUN)') end end @@ -451,9 +451,8 @@ describe Mastodon::CLI::Accounts do let(:options) { { email: '404@example.com' } } it 'exits with an error message indicating that no user was found' do - expect { cli.invoke(:delete, nil, options) }.to output( - a_string_including('No user with such email') - ).to_stdout + expect { subject } + .to output_results('No user with such email') .and raise_error(SystemExit) end end @@ -461,6 +460,7 @@ describe Mastodon::CLI::Accounts do end describe '#approve' do + let(:action) { :approve } let(:total_users) { 4 } before do @@ -469,8 +469,10 @@ describe Mastodon::CLI::Accounts do end context 'with --all option' do + let(:options) { { all: true } } + it 'approves all pending registrations' do - cli.invoke(:approve, nil, all: true) + subject expect(User.pluck(:approved).all?(true)).to be(true) end @@ -481,7 +483,7 @@ describe Mastodon::CLI::Accounts do let(:options) { { number: 2 } } it 'approves the earliest n pending registrations' do - cli.invoke(:approve, nil, options) + subject n_earliest_pending_registrations = User.order(created_at: :asc).first(options[:number]) @@ -489,7 +491,7 @@ describe Mastodon::CLI::Accounts do end it 'does not approve the remaining pending registrations' do - cli.invoke(:approve, nil, options) + subject pending_registrations = User.order(created_at: :asc).last(total_users - options[:number]) @@ -498,10 +500,11 @@ describe Mastodon::CLI::Accounts do end context 'when the number is negative' do + let(:options) { { number: -1 } } + it 'exits with an error message indicating that the number must be positive' do - expect { cli.invoke(:approve, nil, number: -1) }.to output( - a_string_including('Number must be positive') - ).to_stdout + expect { subject } + .to output_results('Number must be positive') .and raise_error(SystemExit) end end @@ -510,13 +513,13 @@ describe Mastodon::CLI::Accounts do let(:options) { { number: total_users * 2 } } it 'approves all users' do - cli.invoke(:approve, nil, options) + subject expect(User.pluck(:approved).all?(true)).to be(true) end it 'does not raise any error' do - expect { cli.invoke(:approve, nil, options) } + expect { subject } .to_not raise_error end end @@ -528,7 +531,7 @@ describe Mastodon::CLI::Accounts do let(:arguments) { [user.account.username] } it 'approves the specified user successfully' do - cli.invoke(:approve, arguments) + subject expect(user.reload.approved?).to be(true) end @@ -538,9 +541,8 @@ describe Mastodon::CLI::Accounts do let(:arguments) { ['non_existent_username'] } it 'exits with an error message indicating that no such account was found' do - expect { cli.invoke(:approve, arguments) }.to output( - a_string_including('No such account') - ).to_stdout + expect { subject } + .to output_results('No such account') .and raise_error(SystemExit) end end @@ -548,13 +550,14 @@ describe Mastodon::CLI::Accounts do end describe '#follow' do + let(:action) { :follow } + context 'when the given username is not found' do let(:arguments) { ['non_existent_username'] } it 'exits with an error message indicating that no account with the given username was found' do - expect { cli.invoke(:follow, arguments) }.to output( - a_string_including('No such account') - ).to_stdout + expect { subject } + .to output_results('No such account') .and raise_error(SystemExit) end end @@ -565,6 +568,7 @@ describe Mastodon::CLI::Accounts do let!(:follower_rony) { Fabricate(:account, username: 'rony') } let!(:follower_charles) { Fabricate(:account, username: 'charles') } let(:follow_service) { instance_double(FollowService, call: nil) } + let(:arguments) { [target_account.username] } before do allow(FollowService).to receive(:new).and_return(follow_service) @@ -572,7 +576,7 @@ describe Mastodon::CLI::Accounts do end it 'makes all local accounts follow the target account' do - cli.follow(target_account.username) + subject expect(follow_service).to have_received(:call).with(follower_bob, target_account, any_args).once expect(follow_service).to have_received(:call).with(follower_rony, target_account, any_args).once @@ -580,21 +584,21 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message' do - expect { cli.follow(target_account.username) }.to output( - a_string_including("OK, followed target from #{Account.local.count} accounts") - ).to_stdout + expect { subject } + .to output_results("OK, followed target from #{Account.local.count} accounts") end end end describe '#unfollow' do + let(:action) { :unfollow } + context 'when the given username is not found' do let(:arguments) { ['non_existent_username'] } it 'exits with an error message indicating that no account with the given username was found' do - expect { cli.invoke(:unfollow, arguments) }.to output( - a_string_including('No such account') - ).to_stdout + expect { subject } + .to output_results('No such account') .and raise_error(SystemExit) end end @@ -605,6 +609,7 @@ describe Mastodon::CLI::Accounts do let!(:follower_rambo) { Fabricate(:account, username: 'rambo', domain: nil) } let!(:follower_ana) { Fabricate(:account, username: 'ana', domain: nil) } let(:unfollow_service) { instance_double(UnfollowService, call: nil) } + let(:arguments) { [target_account.username] } before do accounts = [follower_chris, follower_rambo, follower_ana] @@ -614,7 +619,7 @@ describe Mastodon::CLI::Accounts do end it 'makes all local accounts unfollow the target account' do - cli.unfollow(target_account.username) + subject expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once @@ -622,21 +627,21 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message' do - expect { cli.unfollow(target_account.username) }.to output( - a_string_including('OK, unfollowed target from 3 accounts') - ).to_stdout + expect { subject } + .to output_results('OK, unfollowed target from 3 accounts') end end end describe '#backup' do + let(:action) { :backup } + context 'when the given username is not found' do let(:arguments) { ['non_existent_username'] } it 'exits with an error message indicating that there is no such account' do - expect { cli.invoke(:backup, arguments) }.to output( - a_string_including('No user with such username') - ).to_stdout + expect { subject } + .to output_results('No user with such username') .and raise_error(SystemExit) end end @@ -647,22 +652,21 @@ describe Mastodon::CLI::Accounts do let(:arguments) { [account.username] } it 'creates a new backup for the specified user' do - expect { cli.invoke(:backup, arguments) }.to change { user.backups.count }.by(1) + expect { subject }.to change { user.backups.count }.by(1) end it 'creates a backup job' do allow(BackupWorker).to receive(:perform_async) - cli.invoke(:backup, arguments) + subject latest_backup = user.backups.last expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once end it 'displays a successful message' do - expect { cli.invoke(:backup, arguments) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') end end end @@ -724,9 +728,8 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message' do - expect { cli.refresh }.to output( - a_string_including('Refreshed 2 accounts') - ).to_stdout + expect { cli.refresh } + .to output_results('Refreshed 2 accounts') end context 'with --dry-run option' do @@ -761,9 +764,8 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message with (DRY RUN)' do - expect { cli.refresh }.to output( - a_string_including('Refreshed 2 accounts (DRY RUN)') - ).to_stdout + expect { cli.refresh } + .to output_results('Refreshed 2 accounts (DRY RUN)') end end end @@ -823,9 +825,7 @@ describe Mastodon::CLI::Accounts do allow(account_example_com_a).to receive(:reset_avatar!).and_raise(Mastodon::UnexpectedResponseError) expect { cli.refresh(*arguments) } - .to output( - a_string_including("Account failed: #{account_example_com_a.username}@#{account_example_com_a.domain}") - ).to_stdout + .to output_results("Account failed: #{account_example_com_a.username}@#{account_example_com_a.domain}") end end @@ -833,9 +833,8 @@ describe Mastodon::CLI::Accounts do it 'exits with an error message' do allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(nil) - expect { cli.refresh(*arguments) }.to output( - a_string_including('No such account') - ).to_stdout + expect { cli.refresh(*arguments) } + .to output_results('No such account') .and raise_error(SystemExit) end end @@ -878,7 +877,6 @@ describe Mastodon::CLI::Accounts do allow(cli).to receive(:parallelize_with_progress).and_yield(account_example_com_a) .and_yield(account_example_com_b) .and_return([2, nil]) - cli.options = { domain: domain } end @@ -925,32 +923,33 @@ describe Mastodon::CLI::Accounts do context 'when neither a list of accts nor options are provided' do it 'exits with an error message' do - expect { cli.refresh }.to output( - a_string_including('No account(s) given') - ).to_stdout + expect { cli.refresh } + .to output_results('No account(s) given') .and raise_error(SystemExit) end end end describe '#rotate' do + let(:action) { :rotate } + context 'when neither username nor --all option are given' do it 'exits with an error message' do - expect { cli.rotate }.to output( - a_string_including('No account(s) given') - ).to_stdout + expect { subject } + .to output_results('No account(s) given') .and raise_error(SystemExit) end end context 'when a username is given' do let(:account) { Fabricate(:account) } + let(:arguments) { [account.username] } it 'correctly rotates keys for the specified account' do old_private_key = account.private_key old_public_key = account.public_key - cli.rotate(account.username) + subject account.reload expect(account.private_key).to_not eq(old_private_key) @@ -960,16 +959,17 @@ describe Mastodon::CLI::Accounts do it 'broadcasts the new keys for the specified account' do allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in) - cli.rotate(account.username) + subject expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once end context 'when the given username is not found' do + let(:arguments) { ['non_existent_username'] } + it 'exits with an error message when the specified username is not found' do - expect { cli.rotate('non_existent_username') }.to output( - a_string_including('No such account') - ).to_stdout + expect { subject } + .to output_results('No such account') .and raise_error(SystemExit) end end @@ -977,17 +977,13 @@ describe Mastodon::CLI::Accounts do context 'when --all option is provided' do let!(:accounts) { Fabricate.times(2, :account) } - let(:options) { { all: true } } - - before do - cli.options = { all: true } - end + let(:options) { { all: true } } it 'correctly rotates keys for all local accounts' do old_private_keys = accounts.map(&:private_key) old_public_keys = accounts.map(&:public_key) - cli.rotate + subject accounts.each(&:reload) expect(accounts.map(&:private_key)).to_not eq(old_private_keys) @@ -997,7 +993,7 @@ describe Mastodon::CLI::Accounts do it 'broadcasts the new keys for each account' do allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in) - cli.rotate + subject accounts.each do |account| expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once @@ -1007,11 +1003,12 @@ describe Mastodon::CLI::Accounts do end describe '#merge' do + let(:action) { :merge } + shared_examples 'an account not found' do |acct| it 'exits with an error message indicating that there is no such account' do - expect { cli.invoke(:merge, arguments) }.to output( - a_string_including("No such account (#{acct})") - ).to_stdout + expect { subject } + .to output_results("No such account (#{acct})") .and raise_error(SystemExit) end end @@ -1061,9 +1058,8 @@ describe Mastodon::CLI::Accounts do end it 'exits with an error message indicating that the accounts do not have the same pub key' do - expect { cli.invoke(:merge, arguments) }.to output( - a_string_including("Accounts don't have the same public key, might not be duplicates!\nOverride with --force") - ).to_stdout + expect { subject } + .to output_results("Accounts don't have the same public key, might not be duplicates!\nOverride with --force") .and raise_error(SystemExit) end @@ -1076,13 +1072,13 @@ describe Mastodon::CLI::Accounts do end it 'merges "from_account" into "to_account"' do - cli.invoke(:merge, arguments, options) + subject expect(to_account).to have_received(:merge_with!).with(from_account).once end it 'deletes "from_account"' do - cli.invoke(:merge, arguments, options) + subject expect(from_account).to have_received(:destroy).once end @@ -1104,13 +1100,13 @@ describe Mastodon::CLI::Accounts do end it 'merges "from_account" into "to_account"' do - cli.invoke(:merge, arguments) + subject expect(to_account).to have_received(:merge_with!).with(from_account).once end it 'deletes "from_account"' do - cli.invoke(:merge, arguments) + subject expect(from_account).to have_received(:destroy) end @@ -1118,6 +1114,7 @@ describe Mastodon::CLI::Accounts do end describe '#cull' do + let(:action) { :cull } let(:delete_account_service) { instance_double(DeleteAccountService, call: nil) } let!(:tom) { Fabricate(:account, updated_at: 30.days.ago, username: 'tom', uri: 'https://example.com/users/tom', domain: 'example.com', protocol: :activitypub) } let!(:bob) { Fabricate(:account, updated_at: 30.days.ago, last_webfingered_at: nil, username: 'bob', uri: 'https://example.org/users/bob', domain: 'example.org', protocol: :activitypub) } @@ -1138,14 +1135,14 @@ describe Mastodon::CLI::Accounts do end it 'deletes all inactive remote accounts that longer exist in the origin server' do - cli.cull + subject expect(delete_account_service).to have_received(:call).with(bob, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once end it 'does not delete any active remote account that still exists in the origin server' do - cli.cull + subject expect(delete_account_service).to_not have_received(:call).with(tom, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(ana, reserve_username: false) @@ -1153,18 +1150,17 @@ describe Mastodon::CLI::Accounts do end it 'touches inactive remote accounts that have not been deleted' do - expect { cli.cull }.to(change { tales.reload.updated_at }) + expect { subject }.to(change { tales.reload.updated_at }) end it 'displays the summary correctly' do - expect { cli.cull }.to output( - a_string_including('Visited 5 accounts, removed 2') - ).to_stdout + expect { subject } + .to output_results('Visited 5 accounts, removed 2') end end context 'when a domain is specified' do - let(:domain) { 'example.net' } + let(:arguments) { ['example.net'] } before do stub_parallelize_with_progress! @@ -1173,16 +1169,15 @@ describe Mastodon::CLI::Accounts do end it 'deletes inactive remote accounts that longer exist in the specified domain' do - cli.cull(domain) + subject expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(tales, reserve_username: false).once end it 'displays the summary correctly' do - expect { cli.cull(domain) }.to output( - a_string_including('Visited 2 accounts, removed 2') - ).to_stdout + expect { subject } + .to output_results('Visited 2 accounts, removed 2') end end @@ -1195,15 +1190,14 @@ describe Mastodon::CLI::Accounts do end it 'skips accounts from the unavailable domain' do - cli.cull + subject expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) end it 'displays the summary correctly' do - expect { cli.cull }.to output( - a_string_including("Visited 5 accounts, removed 0\nThe following domains were not available during the check:\n example.net") - ).to_stdout + expect { subject } + .to output_results("Visited 5 accounts, removed 0\nThe following domains were not available during the check:\n example.net") end end @@ -1242,25 +1236,25 @@ describe Mastodon::CLI::Accounts do end describe '#reset_relationships' do + let(:action) { :reset_relationships } let(:target_account) { Fabricate(:account) } let(:arguments) { [target_account.username] } context 'when no option is given' do it 'exits with an error message indicating that at least one option is required' do - expect { cli.invoke(:reset_relationships, arguments) }.to output( - a_string_including('Please specify either --follows or --followers, or both') - ).to_stdout + expect { subject } + .to output_results('Please specify either --follows or --followers, or both') .and raise_error(SystemExit) end end context 'when the given username is not found' do let(:arguments) { ['non_existent_username'] } + let(:options) { { follows: true } } it 'exits with an error message indicating that there is no such account' do - expect { cli.invoke(:reset_relationships, arguments, follows: true) }.to output( - a_string_including('No such account') - ).to_stdout + expect { subject } + .to output_results('No such account') .and raise_error(SystemExit) end end @@ -1277,7 +1271,7 @@ describe Mastodon::CLI::Accounts do end it 'resets all "following" relationships from the target account' do - cli.invoke(:reset_relationships, arguments, options) + subject expect(target_account.reload.following).to be_empty end @@ -1285,15 +1279,14 @@ describe Mastodon::CLI::Accounts do it 'calls BootstrapTimelineWorker once to rebuild the timeline' do allow(BootstrapTimelineWorker).to receive(:perform_async) - cli.invoke(:reset_relationships, arguments, options) + subject expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once end it 'displays a successful message' do - expect { cli.invoke(:reset_relationships, arguments, options) }.to output( - a_string_including("Processed #{total_relationships} relationships") - ).to_stdout + expect { subject } + .to output_results("Processed #{total_relationships} relationships") end end @@ -1305,15 +1298,14 @@ describe Mastodon::CLI::Accounts do end it 'resets all "followers" relationships from the target account' do - cli.invoke(:reset_relationships, arguments, options) + subject expect(target_account.reload.followers).to be_empty end it 'displays a successful message' do - expect { cli.invoke(:reset_relationships, arguments, options) }.to output( - a_string_including("Processed #{total_relationships} relationships") - ).to_stdout + expect { subject } + .to output_results("Processed #{total_relationships} relationships") end end @@ -1326,13 +1318,13 @@ describe Mastodon::CLI::Accounts do end it 'resets all "followers" relationships from the target account' do - cli.invoke(:reset_relationships, arguments, options) + subject expect(target_account.reload.followers).to be_empty end it 'resets all "following" relationships from the target account' do - cli.invoke(:reset_relationships, arguments, options) + subject expect(target_account.reload.following).to be_empty end @@ -1340,21 +1332,21 @@ describe Mastodon::CLI::Accounts do it 'calls BootstrapTimelineWorker once to rebuild the timeline' do allow(BootstrapTimelineWorker).to receive(:perform_async) - cli.invoke(:reset_relationships, arguments, options) + subject expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once end it 'displays a successful message' do - expect { cli.invoke(:reset_relationships, arguments, options) }.to output( - a_string_including("Processed #{total_relationships} relationships") - ).to_stdout + expect { subject } + .to output_results("Processed #{total_relationships} relationships") end end end end describe '#prune' do + let(:action) { :prune } let!(:local_account) { Fabricate(:account) } let!(:bot_account) { Fabricate(:account, bot: true, domain: 'example.com') } let!(:group_account) { Fabricate(:account, actor_type: 'Group', domain: 'example.com') } @@ -1369,7 +1361,7 @@ describe Mastodon::CLI::Accounts do end it 'prunes all remote accounts with no interactions with local users' do - cli.prune + subject prunable_account_ids = prunable_accounts.pluck(:id) @@ -1377,42 +1369,39 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message' do - expect { cli.prune }.to output( - a_string_including("OK, pruned #{prunable_accounts.size} accounts") - ).to_stdout + expect { subject } + .to output_results("OK, pruned #{prunable_accounts.size} accounts") end it 'does not prune local accounts' do - cli.prune + subject expect(Account.exists?(id: local_account.id)).to be(true) end it 'does not prune bot accounts' do - cli.prune + subject expect(Account.exists?(id: bot_account.id)).to be(true) end it 'does not prune group accounts' do - cli.prune + subject expect(Account.exists?(id: group_account.id)).to be(true) end it 'does not prune accounts that have been mentioned' do - cli.prune + subject expect(Account.exists?(id: mentioned_account.id)).to be true end context 'with --dry-run option' do - before do - cli.options = { dry_run: true } - end + let(:options) { { dry_run: true } } it 'does not prune any account' do - cli.prune + subject prunable_account_ids = prunable_accounts.pluck(:id) @@ -1420,14 +1409,14 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message with (DRY RUN)' do - expect { cli.prune }.to output( - a_string_including("OK, pruned #{prunable_accounts.size} accounts (DRY RUN)") - ).to_stdout + expect { subject } + .to output_results("OK, pruned #{prunable_accounts.size} accounts (DRY RUN)") end end end describe '#migrate' do + let(:action) { :migrate } let!(:source_account) { Fabricate(:account) } let!(:target_account) { Fabricate(:account, domain: 'example.com') } let(:arguments) { [source_account.username] } @@ -1441,7 +1430,7 @@ describe Mastodon::CLI::Accounts do shared_examples 'a successful migration' do it 'calls the MoveService for the last migration' do - cli.invoke(:migrate, arguments, options) + subject last_migration = source_account.migrations.last @@ -1449,9 +1438,8 @@ describe Mastodon::CLI::Accounts do end it 'displays a successful message' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including("OK, migrated #{source_account.acct} to #{target_account.acct}") - ).to_stdout + expect { subject } + .to output_results("OK, migrated #{source_account.acct} to #{target_account.acct}") end end @@ -1459,29 +1447,27 @@ describe Mastodon::CLI::Accounts do let(:options) { { replay: true, target: "#{target_account.username}@example.com" } } it 'exits with an error message indicating that using both options is not possible' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including('Use --replay or --target, not both') - ).to_stdout + expect { subject } + .to output_results('Use --replay or --target, not both') .and raise_error(SystemExit) end end context 'when no option is given' do it 'exits with an error message indicating that at least one option must be used' do - expect { cli.invoke(:migrate, arguments, {}) }.to output( - a_string_including('Use either --replay or --target') - ).to_stdout + expect { subject } + .to output_results('Use either --replay or --target') .and raise_error(SystemExit) end end context 'when the given username is not found' do let(:arguments) { ['non_existent_username'] } + let(:options) { { replay: true } } it 'exits with an error message indicating that there is no such account' do - expect { cli.invoke(:migrate, arguments, replay: true) }.to output( - a_string_including("No such account: #{arguments.first}") - ).to_stdout + expect { subject } + .to output_results("No such account: #{arguments.first}") .and raise_error(SystemExit) end end @@ -1491,9 +1477,8 @@ describe Mastodon::CLI::Accounts do context 'when the specified account has no previous migrations' do it 'exits with an error message indicating that the given account has no previous migrations' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including('The specified account has not performed any migration') - ).to_stdout + expect { subject } + .to output_results('The specified account has not performed any migration') .and raise_error(SystemExit) end end @@ -1515,9 +1500,8 @@ describe Mastodon::CLI::Accounts do end it 'exits with an error message' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway') - ).to_stdout + expect { subject } + .to output_results('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway') .and raise_error(SystemExit) end end @@ -1544,9 +1528,8 @@ describe Mastodon::CLI::Accounts do end it 'exits with an error message indicating that there is no such account' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including("The specified target account could not be found: #{options[:target]}") - ).to_stdout + expect { subject } + .to output_results("The specified target account could not be found: #{options[:target]}") .and raise_error(SystemExit) end end @@ -1557,7 +1540,7 @@ describe Mastodon::CLI::Accounts do end it 'creates a migration for the specified account with the target account' do - cli.invoke(:migrate, arguments, options) + subject last_migration = source_account.migrations.last @@ -1569,9 +1552,8 @@ describe Mastodon::CLI::Accounts do context 'when the migration record is invalid' do it 'exits with an error indicating that the validation failed' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including('Error: Validation failed') - ).to_stdout + expect { subject } + .to output_results('Error: Validation failed') .and raise_error(SystemExit) end end @@ -1582,9 +1564,8 @@ describe Mastodon::CLI::Accounts do end it 'exits with an error message' do - expect { cli.invoke(:migrate, arguments, options) }.to output( - a_string_including('The specified account is redirecting to a different target account. Use --force if you want to change the migration target') - ).to_stdout + expect { subject } + .to output_results('The specified account is redirecting to a different target account. Use --force if you want to change the migration target') .and raise_error(SystemExit) end end diff --git a/spec/lib/mastodon/cli/cache_spec.rb b/spec/lib/mastodon/cli/cache_spec.rb index c1ce04710..b1515801e 100644 --- a/spec/lib/mastodon/cli/cache_spec.rb +++ b/spec/lib/mastodon/cli/cache_spec.rb @@ -4,22 +4,29 @@ require 'rails_helper' require 'mastodon/cli/cache' describe Mastodon::CLI::Cache do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#clear' do + let(:action) { :clear } + before { allow(Rails.cache).to receive(:clear) } it 'clears the Rails cache' do - expect { cli.invoke(:clear) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') expect(Rails.cache).to have_received(:clear) end end describe '#recount' do + let(:action) { :recount } + context 'with the `accounts` argument' do let(:arguments) { ['accounts'] } let(:account_stat) { Fabricate(:account_stat) } @@ -29,9 +36,8 @@ describe Mastodon::CLI::Cache do end it 're-calculates account records in the cache' do - expect { cli.invoke(:recount, arguments) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') expect(account_stat.reload.statuses_count).to be_zero end @@ -46,9 +52,8 @@ describe Mastodon::CLI::Cache do end it 're-calculates account records in the cache' do - expect { cli.invoke(:recount, arguments) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') expect(status_stat.reload.replies_count).to be_zero end @@ -58,9 +63,9 @@ describe Mastodon::CLI::Cache do let(:arguments) { ['other-type'] } it 'Exits with an error message' do - expect { cli.invoke(:recount, arguments) }.to output( - a_string_including('Unknown') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('Unknown') + .and raise_error(SystemExit) end end end diff --git a/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb b/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb index 6e4675748..1745ea01b 100644 --- a/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb +++ b/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb @@ -4,42 +4,45 @@ require 'rails_helper' require 'mastodon/cli/canonical_email_blocks' describe Mastodon::CLI::CanonicalEmailBlocks do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#find' do + let(:action) { :find } let(:arguments) { ['user@example.com'] } context 'when a block is present' do before { Fabricate(:canonical_email_block, email: 'user@example.com') } it 'announces the presence of the block' do - expect { cli.invoke(:find, arguments) }.to output( - a_string_including('user@example.com is blocked') - ).to_stdout + expect { subject } + .to output_results('user@example.com is blocked') end end context 'when a block is not present' do it 'announces the absence of the block' do - expect { cli.invoke(:find, arguments) }.to output( - a_string_including('user@example.com is not blocked') - ).to_stdout + expect { subject } + .to output_results('user@example.com is not blocked') end end end describe '#remove' do + let(:action) { :remove } let(:arguments) { ['user@example.com'] } context 'when a block is present' do before { Fabricate(:canonical_email_block, email: 'user@example.com') } it 'removes the block' do - expect { cli.invoke(:remove, arguments) }.to output( - a_string_including('Unblocked user@example.com') - ).to_stdout + expect { subject } + .to output_results('Unblocked user@example.com') expect(CanonicalEmailBlock.matching_email('user@example.com')).to be_empty end @@ -47,9 +50,8 @@ describe Mastodon::CLI::CanonicalEmailBlocks do context 'when a block is not present' do it 'announces the absence of the block' do - expect { cli.invoke(:remove, arguments) }.to output( - a_string_including('user@example.com is not blocked') - ).to_stdout + expect { subject } + .to output_results('user@example.com is not blocked') end end end diff --git a/spec/lib/mastodon/cli/domains_spec.rb b/spec/lib/mastodon/cli/domains_spec.rb index add754159..a10907f76 100644 --- a/spec/lib/mastodon/cli/domains_spec.rb +++ b/spec/lib/mastodon/cli/domains_spec.rb @@ -4,20 +4,26 @@ require 'rails_helper' require 'mastodon/cli/domains' describe Mastodon::CLI::Domains do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#purge' do + let(:action) { :purge } + context 'with accounts from the domain' do - let(:options) { {} } let(:domain) { 'host.example' } let!(:account) { Fabricate(:account, domain: domain) } + let(:arguments) { [domain] } it 'removes the account' do - expect { cli.invoke(:purge, [domain], options) }.to output( - a_string_including('Removed 1 accounts') - ).to_stdout + expect { subject } + .to output_results('Removed 1 accounts') + expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound) end end diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index f5cb6c332..13deb05b6 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -4,96 +4,99 @@ require 'rails_helper' require 'mastodon/cli/email_domain_blocks' describe Mastodon::CLI::EmailDomainBlocks do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#list' do + let(:action) { :list } + context 'with email domain block records' do let!(:parent_block) { Fabricate(:email_domain_block) } let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) } - let(:options) { {} } it 'lists the blocks' do - expect { cli.invoke(:list, [], options) }.to output( - a_string_including(parent_block.domain) - .and(a_string_including(child_block.domain)) - ).to_stdout + expect { subject } + .to output_results( + parent_block.domain, + child_block.domain + ) end end end describe '#add' do - context 'without any options' do - let(:options) { {} } + let(:action) { :add } + context 'without any options' do it 'warns about usage and exits' do - expect { cli.invoke(:add, [], options) }.to output( - a_string_including('No domain(s) given') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('No domain(s) given') + .and raise_error(SystemExit) end end context 'when blocks exist' do let(:options) { {} } let(:domain) { 'host.example' } + let(:arguments) { [domain] } before { Fabricate(:email_domain_block, domain: domain) } it 'does not add a new block' do - expect { cli.invoke(:add, [domain], options) }.to output( - a_string_including('is already blocked') - ).to_stdout + expect { subject } + .to output_results('is already blocked') .and(not_change(EmailDomainBlock, :count)) end end context 'when no blocks exist' do - let(:options) { {} } let(:domain) { 'host.example' } + let(:arguments) { [domain] } it 'adds a new block' do - expect { cli.invoke(:add, [domain], options) }.to output( - a_string_including('Added 1') - ).to_stdout + expect { subject } + .to output_results('Added 1') .and(change(EmailDomainBlock, :count).by(1)) end end end describe '#remove' do - context 'without any options' do - let(:options) { {} } + let(:action) { :remove } + context 'without any options' do it 'warns about usage and exits' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('No domain(s) given') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('No domain(s) given') + .and raise_error(SystemExit) end end context 'when blocks exist' do - let(:options) { {} } let(:domain) { 'host.example' } + let(:arguments) { [domain] } before { Fabricate(:email_domain_block, domain: domain) } it 'removes the block' do - expect { cli.invoke(:remove, [domain], options) }.to output( - a_string_including('Removed 1') - ).to_stdout + expect { subject } + .to output_results('Removed 1') .and(change(EmailDomainBlock, :count).by(-1)) end end context 'when no blocks exist' do - let(:options) { {} } let(:domain) { 'host.example' } + let(:arguments) { [domain] } it 'does not remove a block' do - expect { cli.invoke(:remove, [domain], options) }.to output( - a_string_including('is not yet blocked') - ).to_stdout + expect { subject } + .to output_results('is not yet blocked') .and(not_change(EmailDomainBlock, :count)) end end diff --git a/spec/lib/mastodon/cli/emoji_spec.rb b/spec/lib/mastodon/cli/emoji_spec.rb index 3441413b9..d05e972e7 100644 --- a/spec/lib/mastodon/cli/emoji_spec.rb +++ b/spec/lib/mastodon/cli/emoji_spec.rb @@ -4,10 +4,10 @@ require 'rails_helper' require 'mastodon/cli/emoji' describe Mastodon::CLI::Emoji do - subject { cli.invoke(action, args, options) } + subject { cli.invoke(action, arguments, options) } let(:cli) { described_class.new } - let(:args) { [] } + let(:arguments) { [] } let(:options) { {} } it_behaves_like 'CLI Command' @@ -29,7 +29,7 @@ describe Mastodon::CLI::Emoji do context 'with existing custom emoji' do let(:import_path) { Rails.root.join('spec', 'fixtures', 'files', 'elite-assets.tar.gz') } let(:action) { :import } - let(:args) { [import_path] } + let(:arguments) { [import_path] } it 'reports about imported emoji' do expect { subject } @@ -51,7 +51,7 @@ describe Mastodon::CLI::Emoji do after { FileUtils.rm_rf(export_path.dirname) } let(:export_path) { Rails.root.join('tmp', 'cli-tests', 'export.tar.gz') } - let(:args) { [export_path.dirname.to_s] } + let(:arguments) { [export_path.dirname.to_s] } let(:action) { :export } it 'reports about exported emoji' do @@ -61,8 +61,4 @@ describe Mastodon::CLI::Emoji do end end end - - def output_results(string) - output(a_string_including(string)).to_stdout - end end diff --git a/spec/lib/mastodon/cli/feeds_spec.rb b/spec/lib/mastodon/cli/feeds_spec.rb index e16113c85..199798052 100644 --- a/spec/lib/mastodon/cli/feeds_spec.rb +++ b/spec/lib/mastodon/cli/feeds_spec.rb @@ -4,20 +4,25 @@ require 'rails_helper' require 'mastodon/cli/feeds' describe Mastodon::CLI::Feeds do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#build' do + let(:action) { :build } + before { Fabricate(:account) } context 'with --all option' do let(:options) { { all: true } } it 'regenerates feeds for all accounts' do - expect { cli.invoke(:build, [], options) }.to output( - a_string_including('Regenerated feeds') - ).to_stdout + expect { subject } + .to output_results('Regenerated feeds') end end @@ -27,9 +32,8 @@ describe Mastodon::CLI::Feeds do let(:arguments) { ['alice'] } it 'regenerates feeds for the account' do - expect { cli.invoke(:build, arguments) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') end end @@ -37,22 +41,23 @@ describe Mastodon::CLI::Feeds do let(:arguments) { ['invalid-username'] } it 'displays an error and exits' do - expect { cli.invoke(:build, arguments) }.to output( - a_string_including('No such account') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('No such account') + .and raise_error(SystemExit) end end end describe '#clear' do + let(:action) { :clear } + before do allow(redis).to receive(:del).with(key_namespace) end it 'clears the redis `feed:*` namespace' do - expect { cli.invoke(:clear) }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') expect(redis).to have_received(:del).with(key_namespace).once end diff --git a/spec/lib/mastodon/cli/ip_blocks_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb index 684314dc7..dc967a69c 100644 --- a/spec/lib/mastodon/cli/ip_blocks_spec.rb +++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb @@ -4,11 +4,16 @@ require 'rails_helper' require 'mastodon/cli/ip_blocks' describe Mastodon::CLI::IpBlocks do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#add' do + let(:action) { :add } let(:ip_list) do [ '192.0.2.1', @@ -25,10 +30,11 @@ describe Mastodon::CLI::IpBlocks do ] end let(:options) { { severity: 'no_access' } } + let(:arguments) { ip_list } shared_examples 'ip address blocking' do it 'blocks all specified IP addresses' do - cli.invoke(:add, ip_list, options) + subject blocked_ip_addresses = IpBlock.where(ip: ip_list).pluck(:ip) expected_ip_addresses = ip_list.map { |ip| IPAddr.new(ip) } @@ -37,7 +43,7 @@ describe Mastodon::CLI::IpBlocks do end it 'sets the severity for all blocked IP addresses' do - cli.invoke(:add, ip_list, options) + subject blocked_ips_severity = IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity]) @@ -45,9 +51,8 @@ describe Mastodon::CLI::IpBlocks do end it 'displays a success message with a summary' do - expect { cli.invoke(:add, ip_list, options) }.to output( - a_string_including("Added #{ip_list.size}, skipped 0, failed 0") - ).to_stdout + expect { subject } + .to output_results("Added #{ip_list.size}, skipped 0, failed 0") end end @@ -57,19 +62,19 @@ describe Mastodon::CLI::IpBlocks do context 'when a specified IP address is already blocked' do let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) } + let(:arguments) { ip_list } it 'skips the already blocked IP address' do allow(IpBlock).to receive(:new).and_call_original - cli.invoke(:add, ip_list, options) + subject expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last) end it 'displays the correct summary' do - expect { cli.invoke(:add, ip_list, options) }.to output( - a_string_including("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0") - ).to_stdout + expect { subject } + .to output_results("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0") end context 'with --force option' do @@ -77,7 +82,7 @@ describe Mastodon::CLI::IpBlocks do let(:options) { { severity: 'sign_up_requires_approval', force: true } } it 'overwrites the existing IP block record' do - expect { cli.invoke(:add, ip_list, options) } + expect { subject } .to change { blocked_ip.reload.severity } .from('no_access') .to('sign_up_requires_approval') @@ -89,11 +94,11 @@ describe Mastodon::CLI::IpBlocks do context 'when a specified IP address is invalid' do let(:ip_list) { ['320.15.175.0', '9.5.105.255', '0.0.0.0'] } + let(:arguments) { ip_list } it 'displays the correct summary' do - expect { cli.invoke(:add, ip_list, options) }.to output( - a_string_including("#{ip_list.first} is invalid\nAdded #{ip_list.size - 1}, skipped 0, failed 1") - ).to_stdout + expect { subject } + .to output_results("#{ip_list.first} is invalid\nAdded #{ip_list.size - 1}, skipped 0, failed 1") end end @@ -124,6 +129,7 @@ describe Mastodon::CLI::IpBlocks do context 'when a specified IP address fails to be blocked' do let(:ip_address) { '127.0.0.1' } let(:ip_block) { instance_double(IpBlock, ip: ip_address, save: false) } + let(:arguments) { [ip_address] } before do allow(IpBlock).to receive(:new).and_return(ip_block) @@ -132,24 +138,25 @@ describe Mastodon::CLI::IpBlocks do end it 'displays an error message' do - expect { cli.invoke(:add, [ip_address], options) } - .to output( - a_string_including("#{ip_address} could not be saved") - ).to_stdout + expect { subject } + .to output_results("#{ip_address} could not be saved") end end context 'when no IP address is provided' do + let(:arguments) { [] } + it 'exits with an error message' do - expect { cli.add }.to output( - a_string_including('No IP(s) given') - ).to_stdout + expect { subject } + .to output_results('No IP(s) given') .and raise_error(SystemExit) end end end describe '#remove' do + let(:action) { :remove } + context 'when removing exact matches' do let(:ip_list) do [ @@ -166,21 +173,21 @@ describe Mastodon::CLI::IpBlocks do '::/128', ] end + let(:arguments) { ip_list } before do ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) } end it 'removes exact IP blocks' do - cli.invoke(:remove, ip_list) + subject expect(IpBlock.where(ip: ip_list)).to_not exist end it 'displays success message with a summary' do - expect { cli.invoke(:remove, ip_list) }.to output( - a_string_including("Removed #{ip_list.size}, skipped 0") - ).to_stdout + expect { subject } + .to output_results("Removed #{ip_list.size}, skipped 0") end end @@ -192,13 +199,13 @@ describe Mastodon::CLI::IpBlocks do let(:options) { { force: true } } it 'removes blocks for IP ranges that cover given IP(s)' do - cli.invoke(:remove, arguments, options) + subject expect(IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id])).to_not exist end it 'does not remove other IP ranges' do - cli.invoke(:remove, arguments, options) + subject expect(IpBlock.where(id: third_ip_range_block.id)).to exist end @@ -206,47 +213,46 @@ describe Mastodon::CLI::IpBlocks do context 'when a specified IP address is not blocked' do let(:unblocked_ip) { '192.0.2.1' } + let(:arguments) { [unblocked_ip] } it 'skips the IP address' do - expect { cli.invoke(:remove, [unblocked_ip]) }.to output( - a_string_including("#{unblocked_ip} is not yet blocked") - ).to_stdout + expect { subject } + .to output_results("#{unblocked_ip} is not yet blocked") end it 'displays the summary correctly' do - expect { cli.invoke(:remove, [unblocked_ip]) }.to output( - a_string_including('Removed 0, skipped 1') - ).to_stdout + expect { subject } + .to output_results('Removed 0, skipped 1') end end context 'when a specified IP address is invalid' do let(:invalid_ip) { '320.15.175.0' } + let(:arguments) { [invalid_ip] } it 'skips the invalid IP address' do - expect { cli.invoke(:remove, [invalid_ip]) }.to output( - a_string_including("#{invalid_ip} is invalid") - ).to_stdout + expect { subject } + .to output_results("#{invalid_ip} is invalid") end it 'displays the summary correctly' do - expect { cli.invoke(:remove, [invalid_ip]) }.to output( - a_string_including('Removed 0, skipped 1') - ).to_stdout + expect { subject } + .to output_results('Removed 0, skipped 1') end end context 'when no IP address is provided' do it 'exits with an error message' do - expect { cli.remove }.to output( - a_string_including('No IP(s) given') - ).to_stdout + expect { subject } + .to output_results('No IP(s) given') .and raise_error(SystemExit) end end end describe '#export' do + let(:action) { :export } + let(:first_ip_range_block) { IpBlock.create(ip: '192.168.0.0/24', severity: :no_access) } let(:second_ip_range_block) { IpBlock.create(ip: '10.0.0.0/16', severity: :no_access) } let(:third_ip_range_block) { IpBlock.create(ip: '127.0.0.1', severity: :sign_up_block) } @@ -255,15 +261,13 @@ describe Mastodon::CLI::IpBlocks do let(:options) { { format: 'plain' } } it 'exports blocked IPs with "no_access" severity in plain format' do - expect { cli.invoke(:export, nil, options) }.to output( - a_string_including("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") - ).to_stdout + expect { subject } + .to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") end it 'does not export bloked IPs with different severities' do - expect { cli.invoke(:export, nil, options) }.to_not output( - a_string_including("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}") - ).to_stdout + expect { subject } + .to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}") end end @@ -271,23 +275,20 @@ describe Mastodon::CLI::IpBlocks do let(:options) { { format: 'nginx' } } it 'exports blocked IPs with "no_access" severity in plain format' do - expect { cli.invoke(:export, nil, options) }.to output( - a_string_including("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};") - ).to_stdout + expect { subject } + .to output_results("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};") end it 'does not export bloked IPs with different severities' do - expect { cli.invoke(:export, nil, options) }.to_not output( - a_string_including("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};") - ).to_stdout + expect { subject } + .to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};") end end context 'when --format option is not provided' do it 'exports blocked IPs in plain format by default' do - expect { cli.export }.to output( - a_string_including("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") - ).to_stdout + expect { subject } + .to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") end end end diff --git a/spec/lib/mastodon/cli/main_spec.rb b/spec/lib/mastodon/cli/main_spec.rb index b5b5d6906..59f1fc478 100644 --- a/spec/lib/mastodon/cli/main_spec.rb +++ b/spec/lib/mastodon/cli/main_spec.rb @@ -4,13 +4,20 @@ require 'rails_helper' require 'mastodon/cli/main' describe Mastodon::CLI::Main do + subject { cli.invoke(action, arguments, options) } + + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } + it_behaves_like 'CLI Command' - describe 'version' do + describe '#version' do + let(:action) { :version } + it 'returns the Mastodon version' do - expect { described_class.new.invoke(:version) }.to output( - a_string_including(Mastodon::Version.to_s) - ).to_stdout + expect { subject } + .to output_results(Mastodon::Version.to_s) end end end diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb index 95e695ab5..02169b7a4 100644 --- a/spec/lib/mastodon/cli/maintenance_spec.rb +++ b/spec/lib/mastodon/cli/maintenance_spec.rb @@ -4,20 +4,26 @@ require 'rails_helper' require 'mastodon/cli/maintenance' describe Mastodon::CLI::Maintenance do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#fix_duplicates' do + let(:action) { :fix_duplicates } + context 'when the database version is too old' do before do allow(ActiveRecord::Migrator).to receive(:current_version).and_return(2000_01_01_000000) # Earlier than minimum end it 'Exits with error message' do - expect { cli.invoke :fix_duplicates }.to output( - a_string_including('is too old') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('is too old') + .and raise_error(SystemExit) end end @@ -28,9 +34,9 @@ describe Mastodon::CLI::Maintenance do end it 'Exits with error message' do - expect { cli.invoke :fix_duplicates }.to output( - a_string_including('more recent') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('more recent') + .and raise_error(SystemExit) end end @@ -41,9 +47,9 @@ describe Mastodon::CLI::Maintenance do end it 'Exits with error message' do - expect { cli.invoke :fix_duplicates }.to output( - a_string_including('Sidekiq is running') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('Sidekiq is running') + .and raise_error(SystemExit) end end end diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 6d510c1f5..6bbe7e746 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -4,18 +4,24 @@ require 'rails_helper' require 'mastodon/cli/media' describe Mastodon::CLI::Media do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#remove' do + let(:action) { :remove } + context 'with --prune-profiles and --remove-headers' do let(:options) { { prune_profiles: true, remove_headers: true } } it 'warns about usage and exits' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('--prune-profiles and --remove-headers should not be specified simultaneously') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('--prune-profiles and --remove-headers should not be specified simultaneously') + .and raise_error(SystemExit) end end @@ -23,9 +29,9 @@ describe Mastodon::CLI::Media do let(:options) { { include_follows: true } } it 'warns about usage and exits' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('--include-follows can only be used with --prune-profiles or --remove-headers') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('--include-follows can only be used with --prune-profiles or --remove-headers') + .and raise_error(SystemExit) end end @@ -38,9 +44,8 @@ describe Mastodon::CLI::Media do let(:options) { { prune_profiles: true } } it 'removes account avatars' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('Visited 1') - ).to_stdout + expect { subject } + .to output_results('Visited 1') expect(account.reload.avatar).to be_blank end @@ -50,9 +55,8 @@ describe Mastodon::CLI::Media do let(:options) { { remove_headers: true } } it 'removes account header' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('Visited 1') - ).to_stdout + expect { subject } + .to output_results('Visited 1') expect(account.reload.header).to be_blank end @@ -64,9 +68,8 @@ describe Mastodon::CLI::Media do context 'without options' do it 'removes account avatars' do - expect { cli.invoke(:remove) }.to output( - a_string_including('Removed 1') - ).to_stdout + expect { subject } + .to output_results('Removed 1') expect(media_attachment.reload.file).to be_blank expect(media_attachment.reload.thumbnail).to be_blank @@ -76,25 +79,24 @@ describe Mastodon::CLI::Media do end describe '#usage' do - context 'without options' do - let(:options) { {} } + let(:action) { :usage } + context 'without options' do it 'reports about storage size' do - expect { cli.invoke(:usage, [], options) }.to output( - a_string_including('0 Bytes') - ).to_stdout + expect { subject } + .to output_results('0 Bytes') end end end describe '#refresh' do - context 'without any options' do - let(:options) { {} } + let(:action) { :refresh } + context 'without any options' do it 'warns about usage and exits' do - expect { cli.invoke(:refresh, [], options) }.to output( - a_string_including('Specify the source') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('Specify the source') + .and raise_error(SystemExit) end end @@ -108,9 +110,8 @@ describe Mastodon::CLI::Media do let(:status) { Fabricate(:status) } it 'redownloads the attachment file' do - expect { cli.invoke(:refresh, [], options) }.to output( - a_string_including('Downloaded 1 media') - ).to_stdout + expect { subject } + .to output_results('Downloaded 1 media') end end @@ -119,9 +120,9 @@ describe Mastodon::CLI::Media do let(:options) { { account: 'not-real-user@example.host' } } it 'warns about usage and exits' do - expect { cli.invoke(:refresh, [], options) }.to output( - a_string_including('No such account') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('No such account') + .and raise_error(SystemExit) end end @@ -135,9 +136,8 @@ describe Mastodon::CLI::Media do let(:account) { Fabricate(:account) } it 'redownloads the attachment file' do - expect { cli.invoke(:refresh, [], options) }.to output( - a_string_including('Downloaded 1 media') - ).to_stdout + expect { subject } + .to output_results('Downloaded 1 media') end end end @@ -153,9 +153,8 @@ describe Mastodon::CLI::Media do let(:account) { Fabricate(:account, domain: domain) } it 'redownloads the attachment file' do - expect { cli.invoke(:refresh, [], options) }.to output( - a_string_including('Downloaded 1 media') - ).to_stdout + expect { subject } + .to output_results('Downloaded 1 media') end end end diff --git a/spec/lib/mastodon/cli/preview_cards_spec.rb b/spec/lib/mastodon/cli/preview_cards_spec.rb index a766d250e..951ae3758 100644 --- a/spec/lib/mastodon/cli/preview_cards_spec.rb +++ b/spec/lib/mastodon/cli/preview_cards_spec.rb @@ -4,11 +4,17 @@ require 'rails_helper' require 'mastodon/cli/preview_cards' describe Mastodon::CLI::PreviewCards do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#remove' do + let(:action) { :remove } + context 'with relevant preview cards' do before do Fabricate(:preview_card, updated_at: 10.years.ago, type: :link) @@ -18,10 +24,11 @@ describe Mastodon::CLI::PreviewCards do context 'with no arguments' do it 'deletes thumbnails for local preview cards' do - expect { cli.invoke(:remove) }.to output( - a_string_including('Removed 2 preview cards') - .and(a_string_including('approx. 119 KB')) - ).to_stdout + expect { subject } + .to output_results( + 'Removed 2 preview cards', + 'approx. 119 KB' + ) end end @@ -29,10 +36,11 @@ describe Mastodon::CLI::PreviewCards do let(:options) { { link: true } } it 'deletes thumbnails for local preview cards' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('Removed 1 link-type preview cards') - .and(a_string_including('approx. 59.6 KB')) - ).to_stdout + expect { subject } + .to output_results( + 'Removed 1 link-type preview cards', + 'approx. 59.6 KB' + ) end end @@ -40,10 +48,11 @@ describe Mastodon::CLI::PreviewCards do let(:options) { { days: 365 } } it 'deletes thumbnails for local preview cards' do - expect { cli.invoke(:remove, [], options) }.to output( - a_string_including('Removed 1 preview cards') - .and(a_string_including('approx. 59.6 KB')) - ).to_stdout + expect { subject } + .to output_results( + 'Removed 1 preview cards', + 'approx. 59.6 KB' + ) end end end diff --git a/spec/lib/mastodon/cli/settings_spec.rb b/spec/lib/mastodon/cli/settings_spec.rb index 7dcd1110b..02d1042c5 100644 --- a/spec/lib/mastodon/cli/settings_spec.rb +++ b/spec/lib/mastodon/cli/settings_spec.rb @@ -7,59 +7,64 @@ describe Mastodon::CLI::Settings do it_behaves_like 'CLI Command' describe 'subcommand "registrations"' do + subject { cli.invoke(action, arguments, options) } + let(:cli) { Mastodon::CLI::Registrations.new } + let(:arguments) { [] } + let(:options) { {} } before do Setting.registrations_mode = nil end describe '#open' do + let(:action) { :open } + it 'changes "registrations_mode" to "open"' do - expect { cli.open }.to change(Setting, :registrations_mode).from(nil).to('open') + expect { subject }.to change(Setting, :registrations_mode).from(nil).to('open') end it 'displays success message' do - expect { cli.open }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') end end describe '#approved' do + let(:action) { :approved } + it 'changes "registrations_mode" to "approved"' do - expect { cli.approved }.to change(Setting, :registrations_mode).from(nil).to('approved') + expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved') end it 'displays success message' do - expect { cli.approved }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') end context 'with --require-reason' do - before do - cli.options = { require_reason: true } - end + let(:options) { { require_reason: true } } it 'changes "registrations_mode" to "approved"' do - expect { cli.approved }.to change(Setting, :registrations_mode).from(nil).to('approved') + expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved') end it 'sets "require_invite_text" to "true"' do - expect { cli.approved }.to change(Setting, :require_invite_text).from(false).to(true) + expect { subject }.to change(Setting, :require_invite_text).from(false).to(true) end end end describe '#close' do + let(:action) { :close } + it 'changes "registrations_mode" to "none"' do - expect { cli.close }.to change(Setting, :registrations_mode).from(nil).to('none') + expect { subject }.to change(Setting, :registrations_mode).from(nil).to('none') end it 'displays success message' do - expect { cli.close }.to output( - a_string_including('OK') - ).to_stdout + expect { subject } + .to output_results('OK') end end end diff --git a/spec/lib/mastodon/cli/statuses_spec.rb b/spec/lib/mastodon/cli/statuses_spec.rb index 70e4e2c08..63d494bbb 100644 --- a/spec/lib/mastodon/cli/statuses_spec.rb +++ b/spec/lib/mastodon/cli/statuses_spec.rb @@ -4,26 +4,31 @@ require 'rails_helper' require 'mastodon/cli/statuses' describe Mastodon::CLI::Statuses do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#remove', use_transactional_tests: false do + let(:action) { :remove } + context 'with small batch size' do let(:options) { { batch_size: 0 } } it 'exits with error message' do - expect { cli.invoke :remove, [], options }.to output( - a_string_including('Cannot run') - ).to_stdout.and raise_error(SystemExit) + expect { subject } + .to output_results('Cannot run') + .and raise_error(SystemExit) end end context 'with default batch size' do it 'removes unreferenced statuses' do - expect { cli.invoke :remove }.to output( - a_string_including('Done after') - ).to_stdout + expect { subject } + .to output_results('Done after') end end end diff --git a/spec/lib/mastodon/cli/upgrade_spec.rb b/spec/lib/mastodon/cli/upgrade_spec.rb index 0d6494eee..6861e0488 100644 --- a/spec/lib/mastodon/cli/upgrade_spec.rb +++ b/spec/lib/mastodon/cli/upgrade_spec.rb @@ -4,23 +4,26 @@ require 'rails_helper' require 'mastodon/cli/upgrade' describe Mastodon::CLI::Upgrade do + subject { cli.invoke(action, arguments, options) } + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } it_behaves_like 'CLI Command' describe '#storage_schema' do - context 'with records that dont need upgrading' do - let(:options) { {} } + let(:action) { :storage_schema } + context 'with records that dont need upgrading' do before do Fabricate(:account) Fabricate(:media_attachment) end it 'does not upgrade storage for the attachments' do - expect { cli.invoke(:storage_schema, [], options) }.to output( - a_string_including('Upgraded storage schema of 0 records') - ).to_stdout + expect { subject } + .to output_results('Upgraded storage schema of 0 records') end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index d30e7201c..4394b470e 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -88,6 +88,7 @@ RSpec.configure do |config| config.include Chewy::Rspec::Helpers config.include Redisable config.include SignedRequestHelpers, type: :request + config.include CommandLineHelpers, type: :cli config.around(:each, use_transactional_tests: false) do |example| self.use_transactional_tests = false diff --git a/spec/support/command_line_helpers.rb b/spec/support/command_line_helpers.rb new file mode 100644 index 000000000..6f9d63d93 --- /dev/null +++ b/spec/support/command_line_helpers.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module CommandLineHelpers + def output_results(*args) + output( + include(*args) + ).to_stdout + end +end From da3d8aff799e9b92ebe76e2dc8f6f953869a1e42 Mon Sep 17 00:00:00 2001 From: Michael Stanclift <mx@vmstan.com> Date: Thu, 7 Dec 2023 08:40:44 -0600 Subject: [PATCH 41/73] Error handling for attachment batch delete process (#28184) Co-authored-by: Claire <claire.github-309c@sitedethib.com> --- app/lib/attachment_batch.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/lib/attachment_batch.rb b/app/lib/attachment_batch.rb index 13a9da828..b28f5c3d7 100644 --- a/app/lib/attachment_batch.rb +++ b/app/lib/attachment_batch.rb @@ -4,7 +4,8 @@ class AttachmentBatch # Maximum amount of objects you can delete in an S3 API call. It's # important to remember that this does not correspond to the number # of records in the batch, since records can have multiple attachments - LIMIT = 1_000 + LIMIT = ENV.fetch('S3_BATCH_DELETE_LIMIT', 1000).to_i + MAX_RETRY = ENV.fetch('S3_BATCH_DELETE_RETRY', 3).to_i # Attributes generated and maintained by Paperclip (not all of them # are always used on every class, however) @@ -95,6 +96,7 @@ class AttachmentBatch # objects can be processed at once, so we have to potentially # separate them into multiple calls. + retries = 0 keys.each_slice(LIMIT) do |keys_slice| logger.debug { "Deleting #{keys_slice.size} objects" } @@ -102,6 +104,17 @@ class AttachmentBatch objects: keys_slice.map { |key| { key: key } }, quiet: true, }) + rescue => e + retries += 1 + + if retries < MAX_RETRY + logger.debug "Retry #{retries}/#{MAX_RETRY} after #{e.message}" + sleep 2**retries + retry + else + logger.error "Batch deletion from S3 failed after #{e.message}" + raise e + end end end From 3918dc68c7cf7d6f7d2352f9aa4b68d4d02b7d6a Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 7 Dec 2023 09:49:05 -0500 Subject: [PATCH 42/73] Use composite primary key for `PreviewCardsStatus` model (#28208) --- app/models/preview_cards_status.rb | 4 +--- app/models/status.rb | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/models/preview_cards_status.rb b/app/models/preview_cards_status.rb index 341771e4d..214eec22e 100644 --- a/app/models/preview_cards_status.rb +++ b/app/models/preview_cards_status.rb @@ -9,9 +9,7 @@ # url :string # class PreviewCardsStatus < ApplicationRecord - # Composite primary keys are not properly supported in Rails. However, - # we shouldn't need this anyway... - self.primary_key = nil + self.primary_key = [:preview_card_id, :status_id] belongs_to :preview_card belongs_to :status diff --git a/app/models/status.rb b/app/models/status.rb index eb7159dc6..4bdadae06 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -82,8 +82,7 @@ class Status < ApplicationRecord has_and_belongs_to_many :tags - # Because of a composite primary key, the `dependent` option cannot be used on this association - has_one :preview_cards_status, inverse_of: :status # rubocop:disable Rails/HasManyOrHasOneDependent + has_one :preview_cards_status, inverse_of: :status, dependent: :delete has_one :notification, as: :activity, dependent: :destroy has_one :status_stat, inverse_of: :status, dependent: nil @@ -146,7 +145,6 @@ class Status < ApplicationRecord # The `prepend: true` option below ensures this runs before # the `dependent: destroy` callbacks remove relevant records before_destroy :unlink_from_conversations!, prepend: true - before_destroy :reset_preview_card! cache_associated :application, :media_attachments, From 8d8ae05a186bb8a5fca719dc6ea5a057d1aa197f Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Thu, 7 Dec 2023 10:27:41 -0500 Subject: [PATCH 43/73] Add spec coverage for `CLI::Media#lookup` command (#28266) --- spec/lib/mastodon/cli/media_spec.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 6bbe7e746..24e1467a3 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -89,6 +89,32 @@ describe Mastodon::CLI::Media do end end + describe '#lookup' do + let(:action) { :lookup } + let(:arguments) { [url] } + + context 'with valid url not connected to a record' do + let(:url) { 'https://example.host/assets/1' } + + it 'warns about url and exits' do + expect { subject } + .to output_results('Not a media URL') + .and raise_error(SystemExit) + end + end + + context 'with a valid media url' do + let(:status) { Fabricate(:status) } + let(:media_attachment) { Fabricate(:media_attachment, status: status) } + let(:url) { media_attachment.file.url(:original) } + + it 'displays the url of a connected status' do + expect { subject } + .to output_results(status.id.to_s) + end + end + end + describe '#refresh' do let(:action) { :refresh } From 7e514688b3db1a88636ebb24afcaf1d637366f40 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 8 Dec 2023 04:27:33 -0500 Subject: [PATCH 44/73] Convert `api/v2/search` controller spec to request spec (#28269) --- .../api/v2/search_spec.rb} | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) rename spec/{controllers/api/v2/search_controller_spec.rb => requests/api/v2/search_spec.rb} (79%) diff --git a/spec/controllers/api/v2/search_controller_spec.rb b/spec/requests/api/v2/search_spec.rb similarity index 79% rename from spec/controllers/api/v2/search_controller_spec.rb rename to spec/requests/api/v2/search_spec.rb index a16716a10..d0778cba4 100644 --- a/spec/controllers/api/v2/search_controller_spec.rb +++ b/spec/requests/api/v2/search_spec.rb @@ -2,25 +2,21 @@ require 'rails_helper' -RSpec.describe Api::V2::SearchController do - render_views - +describe 'Search API' do context 'with token' do - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') } + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:search' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do + describe 'GET /api/v2/search' do let!(:bob) { Fabricate(:account, username: 'bob_test') } let!(:ana) { Fabricate(:account, username: 'ana_test') } let!(:tom) { Fabricate(:account, username: 'tom_test') } let(:params) { { q: 'test' } } it 'returns http success' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(200) end @@ -29,7 +25,7 @@ RSpec.describe Api::V2::SearchController do let(:params) { { q: 'test', type: 'accounts' } } it 'returns all matching accounts' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(body_as_json[:accounts].pluck(:id)).to contain_exactly(bob.id.to_s, ana.id.to_s, tom.id.to_s) end @@ -38,7 +34,7 @@ RSpec.describe Api::V2::SearchController do let(:params) { { q: 'test1', resolve: '1' } } it 'returns http unauthorized' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(200) end @@ -48,7 +44,7 @@ RSpec.describe Api::V2::SearchController do let(:params) { { q: 'test1', offset: 1 } } it 'returns http unauthorized' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(200) end @@ -62,7 +58,7 @@ RSpec.describe Api::V2::SearchController do end it 'returns only the followed accounts' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(body_as_json[:accounts].pluck(:id)).to contain_exactly(ana.id.to_s) end @@ -73,7 +69,7 @@ RSpec.describe Api::V2::SearchController do before { allow(Search).to receive(:new).and_raise(Mastodon::SyntaxError) } it 'returns http unprocessable_entity' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(422) end @@ -83,7 +79,7 @@ RSpec.describe Api::V2::SearchController do before { allow(Search).to receive(:new).and_raise(ActiveRecord::RecordNotFound) } it 'returns http not_found' do - get :index, params: params + get '/api/v2/search', headers: headers, params: params expect(response).to have_http_status(404) end @@ -92,11 +88,11 @@ RSpec.describe Api::V2::SearchController do end context 'without token' do - describe 'GET #index' do + describe 'GET /api/v2/search' do let(:search_params) { nil } before do - get :index, params: search_params + get '/api/v2/search', params: search_params end context 'without a `q` param' do From 8de86eabbff9d5ff42f2340b1906a8595cd898e8 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 8 Dec 2023 04:51:57 -0500 Subject: [PATCH 45/73] Add spec for `CLI::Domains#crawl` command (#28271) --- lib/mastodon/cli/domains.rb | 6 ++-- spec/lib/mastodon/cli/domains_spec.rb | 47 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/mastodon/cli/domains.rb b/lib/mastodon/cli/domains.rb index 329f17167..e092497dc 100644 --- a/lib/mastodon/cli/domains.rb +++ b/lib/mastodon/cli/domains.rb @@ -97,6 +97,8 @@ module Mastodon::CLI say("Removed #{custom_emojis_count} custom emojis#{dry_run_mode_suffix}", :green) end + CRAWL_SLEEP_TIME = 20 + option :concurrency, type: :numeric, default: 50, aliases: [:c] option :format, type: :string, default: 'summary', aliases: [:f] option :exclude_suspended, type: :boolean, default: false, aliases: [:x] @@ -168,8 +170,8 @@ module Mastodon::CLI pool.post(domain, &work_unit) end - sleep 20 - sleep 20 until pool.queue_length.zero? + sleep CRAWL_SLEEP_TIME + sleep CRAWL_SLEEP_TIME until pool.queue_length.zero? pool.shutdown pool.wait_for_termination(20) diff --git a/spec/lib/mastodon/cli/domains_spec.rb b/spec/lib/mastodon/cli/domains_spec.rb index a10907f76..24f341c12 100644 --- a/spec/lib/mastodon/cli/domains_spec.rb +++ b/spec/lib/mastodon/cli/domains_spec.rb @@ -28,4 +28,51 @@ describe Mastodon::CLI::Domains do end end end + + describe '#crawl' do + let(:action) { :crawl } + + context 'with accounts from the domain' do + let(:domain) { 'host.example' } + + before do + Fabricate(:account, domain: domain) + stub_request(:get, 'https://host.example/api/v1/instance').to_return(status: 200, body: {}.to_json) + stub_request(:get, 'https://host.example/api/v1/instance/peers').to_return(status: 200, body: {}.to_json) + stub_request(:get, 'https://host.example/api/v1/instance/activity').to_return(status: 200, body: {}.to_json) + stub_const('Mastodon::CLI::Domains::CRAWL_SLEEP_TIME', 0) + end + + context 'with --format of summary' do + let(:options) { { format: 'summary' } } + + it 'crawls the domains and summarizes results' do + expect { subject } + .to output_results('Visited 1 domains, 0 failed') + end + end + + context 'with --format of domains' do + let(:options) { { format: 'domains' } } + + it 'crawls the domains and summarizes results' do + expect { subject } + .to output_results(domain) + end + end + + context 'with --format of json' do + let(:options) { { format: 'json' } } + + it 'crawls the domains and summarizes results' do + expect { subject } + .to output_results(json_summary) + end + + def json_summary + Oj.dump('host.example': { activity: {} }) + end + end + end + end end From f37a1535efc7913c0f8b5d2c8e6588ff496c9984 Mon Sep 17 00:00:00 2001 From: JakePaustian <87162217+JakePaustian@users.noreply.github.com> Date: Fri, 8 Dec 2023 03:52:28 -0600 Subject: [PATCH 46/73] Update CONTRIBUTING.md with additional requirements for API additions (#28274) Co-authored-by: Jake Paustian <jake1500@iastate.edu> --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1a5fef79..b68a9bde3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,10 @@ You can contribute in the following ways: If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). +## API Changes and Additions + +Please note that any changes or additions made to the API should have an accompanying pull request on [our documentation repository](https://github.com/mastodon/documentation). + ## Bug reports 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. From ca1b7efb069a3cd4532d0d635f70ea47358714c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 10:52:32 +0100 Subject: [PATCH 47/73] New Crowdin Translations (automated) (#28275) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/fr.json | 2 +- app/javascript/mastodon/locales/hu.json | 4 ++-- app/javascript/mastodon/locales/my.json | 12 ++++++++++++ app/javascript/mastodon/locales/pt-PT.json | 2 +- config/locales/my.yml | 1 + 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 03b656cc4..7db4bf7bc 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -702,7 +702,7 @@ "timeline_hint.resources.followers": "Les abonnés", "timeline_hint.resources.follows": "Les abonnements", "timeline_hint.resources.statuses": "Messages plus anciens", - "trends.counter_by_accounts": "{count, plural, one {{counter} personne} other {{counter} personnes}} au cours {days, plural, one {des dernières 24h} other {des {days} derniers jours}}", + "trends.counter_by_accounts": "{count, plural, one {{counter} pers.} other {{counter} pers.}} sur {days, plural, one {les dernières 24h} other {les {days} derniers jours}}", "trends.trending_now": "Tendance en ce moment", "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.", "units.short.billion": "{count}Md", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index d890e4188..386b15811 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -479,7 +479,7 @@ "onboarding.actions.go_to_home": "Ugrás a saját hírfolyamra", "onboarding.compose.template": "Üdvözlet, #Mastodon!", "onboarding.follows.empty": "Sajnos jelenleg nem jeleníthető meg eredmény. Kipróbálhatod a keresést vagy böngészheted a felfedező oldalon a követni kívánt személyeket, vagy próbáld meg később.", - "onboarding.follows.lead": "A saját hírfolyamod az elsődleges tapasztalás a Mastodonon. Minél több embert követsz, annál aktívabb és érdekesebb a dolog. Az induláshoz itt van néhány javaslat:", + "onboarding.follows.lead": "A kezdőlapod a Mastodon használatának elsődleges módja. Minél több embert követsz, annál aktívabbak és érdekesebbek lesznek a dolgok. Az induláshoz itt van néhány javaslat:", "onboarding.follows.title": "Szabd személyre a kezdőlapodat", "onboarding.profile.discoverable": "Saját profil beállítása felfedezhetőként", "onboarding.profile.discoverable_hint": "A Mastodonon a felfedezhetőség választása esetén a saját bejegyzéseid megjelenhetnek a keresési eredmények és a felkapott tartalmak között, valamint a profilod a hozzád hasonló érdeklődési körrel rendelkező embereknél is ajánlásra kerülhet.", @@ -720,7 +720,7 @@ "upload_form.undo": "Törlés", "upload_form.video_description": "Leírás siket, hallássérült, vak vagy gyengénlátó emberek számára", "upload_modal.analyzing_picture": "Kép elemzése…", - "upload_modal.apply": "Alkalmazás", + "upload_modal.apply": "Alkalmaz", "upload_modal.applying": "Alkalmazás…", "upload_modal.choose_image": "Kép kiválasztása", "upload_modal.description_placeholder": "A gyors, barna róka átugrik a lusta kutya fölött", diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json index 4078a4c06..917419d17 100644 --- a/app/javascript/mastodon/locales/my.json +++ b/app/javascript/mastodon/locales/my.json @@ -21,6 +21,7 @@ "account.blocked": "ဘလော့ထားသည်", "account.browse_more_on_origin_server": "မူရင်းပရိုဖိုင်တွင် ပိုမိုကြည့်ရှုပါ။", "account.cancel_follow_request": "စောင့်ကြည့်မှု ပယ်ဖျက်ခြင်း", + "account.copy": "လင့်ခ်ကို ပရိုဖိုင်သို့ ကူးယူပါ", "account.direct": "@{name} သီးသန့် သိရှိနိုင်အောင် မန်းရှင်းခေါ်မည်", "account.disable_notifications": "@{name} ပို့စ်တင်သည့်အခါ ကျွန်ုပ်ထံ အသိပေးခြင်း မပြုလုပ်ရန်။", "account.domain_blocked": "ဒိုမိန်း ပိတ်ပင်ထားခဲ့သည်", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "ဖတ်ပြီးသားအဖြစ်မှတ်ထားပါ", "conversation.open": "Conversation ကိုကြည့်မည်", "conversation.with": "{အမည်များ} ဖြင့်", + "copy_icon_button.copied": "ကလစ်ဘုတ်သို့ ကူးပါ", "copypaste.copied": "ကူယူပြီးပါပြီ", "copypaste.copy_to_clipboard": "ကလစ်ဘုတ်သို့ ကူးပါ", "directory.federated": "သင် သိသော ဖက်ဒီမှ", @@ -389,6 +391,7 @@ "lists.search": "မိမိဖောလိုးထားသူများမှရှာဖွေမည်", "lists.subheading": "သင့်၏စာရင်းများ", "load_pending": "{count, plural, one {# new item} other {# new items}}", + "loading_indicator.label": "လုပ်ဆောင်နေသည်…", "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}", "moved_to_account_banner.text": "{movedToAccount} အကောင့်သို့ပြောင်းလဲထားသဖြင့် {disabledAccount} အကောင့်မှာပိတ်ထားသည်", "mute_modal.duration": "ကြာချိန်", @@ -477,6 +480,13 @@ "onboarding.follows.empty": "ယခုအချိန် မည်သည့်ရလဒ်ကိုမျှ မပြသနိုင်ပါ။ လူများကိုစောင့်ကြည့်ရန်အတွက် Explore စာမျက်နှာကို အသုံးပြု၍ စမ်းကြည့်နိုင်သည် သို့မဟုတ် နောက်မှ ထပ်စမ်းကြည့်ပါ။", "onboarding.follows.lead": "သင့်ကိုယ်ပိုင်ပို့စ်များ တင်နိုင်သည်။ သင်စောင့်ကြည့်သူ များလေလေ၊ စိတ်ဝင်စားစရာကောင်းသောပို့စ်များ တွေ့ရလေဖြစ်သည်။ ဤပရိုဖိုင်များမှာ ကောင်းမွန်သောအစပြုမှုတစ်ခုဖြစ်ပြီး ၎င်းတို့ကိုစောင့်ကြည့်ခြင်းမှလည်း အချိန်မရွေး ပယ်ဖျက်နိုင်ပါသည်။", "onboarding.follows.title": "Mastodon တွင် ရေပန်းစားခြင်း", + "onboarding.profile.discoverable": "ပရိုဖိုင် ရှာဖွေနိုင်ပါမည်", + "onboarding.profile.display_name": "ဖော်ပြမည့်အမည်", + "onboarding.profile.display_name_hint": "သင့်အမည်အပြည့်အစုံ သို့မဟုတ် သင့်အမည်ပြောင်။", + "onboarding.profile.note": "ကိုယ်ရေးအကျဉ်း", + "onboarding.profile.save_and_continue": "သိမ်းပြီး ဆက်လုပ်ပါ", + "onboarding.profile.title": "ပရိုဖိုင်စနစ် ထည့်သွင်းခြင်း", + "onboarding.profile.upload_avatar": "ပရိုဖိုင်ပုံ အပ်လုဒ်လုပ်ပါ", "onboarding.share.lead": "Mastodon တွင် သင့်အား မည်သို့ရှာတွေ့နိုင်သည်ကို အသိပေးပါ။", "onboarding.share.message": "Mastodon ရှိ ကျွန်ုပ်၏အမည်မှာ {username} ဖြစ်သည်။ ကျွန်ုပ်ကို {url} တွင် စောင့်ကြည့်နိုင်ပါသည်", "onboarding.share.next_steps": "ဖြစ်နိုင်ချေရှိသော နောက်အဆင့်များ -", @@ -520,6 +530,7 @@ "privacy.unlisted.short": "စာရင်းမသွင်းထားပါ", "privacy_policy.last_updated": "နောက်ဆုံး ပြင်ဆင်ခဲ့သည့်ရက်စွဲ {date}", "privacy_policy.title": "ကိုယ်ရေးအချက်အလက်မူဝါဒ", + "recommended": "အကြံပြုသည်", "refresh": "ပြန်လည်စတင်ပါ", "regeneration_indicator.label": "လုပ်ဆောင်နေသည်…", "regeneration_indicator.sublabel": "သင့်ပင်မစာမျက်နှာကို ပြင်ဆင်နေပါသည်။", @@ -590,6 +601,7 @@ "search.quick_action.status_search": "{x} နှင့် ကိုက်ညီသော ပို့စ်များ", "search.search_or_paste": "URL ရိုက်ထည့်ပါ သို့မဟုတ် ရှာဖွေပါ", "search_popout.full_text_search_disabled_message": "{domain} တွင် မရနိုင်ပါ။", + "search_popout.full_text_search_logged_out_message": "အကောင့်ဝင်ထားမှသာ ရနိုင်သည်။", "search_popout.language_code": "ISO ဘာသာစကားကုဒ်", "search_popout.options": "ရွေးချယ်ထားသည်များ ရှာဖွေရန်", "search_popout.quick_actions": "အမြန်လုပ်ဆောင်မှုများ", diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json index 2d1616960..d5055b0dc 100644 --- a/app/javascript/mastodon/locales/pt-PT.json +++ b/app/javascript/mastodon/locales/pt-PT.json @@ -7,7 +7,7 @@ "about.domain_blocks.silenced.explanation": "Normalmente não verá perfis e conteúdo deste servidor, a menos que os procure explicitamente ou opte por os seguir.", "about.domain_blocks.silenced.title": "Limitados", "about.domain_blocks.suspended.explanation": "Nenhum dado deste servidor será processado, armazenado ou trocado, impossibilitando qualquer interação ou comunicação com os utilizadores dessas instâncias.", - "about.domain_blocks.suspended.title": "Supensos", + "about.domain_blocks.suspended.title": "Suspensos", "about.not_available": "Esta informação não foi disponibilizada neste servidor.", "about.powered_by": "Rede social descentralizada baseada no {mastodon}", "about.rules": "Regras do servidor", diff --git a/config/locales/my.yml b/config/locales/my.yml index 03ed771a4..4ba4fcfad 100644 --- a/config/locales/my.yml +++ b/config/locales/my.yml @@ -1324,6 +1324,7 @@ my: '86400': ၁ ရက် expires_in_prompt: ဘယ်တော့မှ generate: ဖိတ်ကြားချက်လင့်ခ် ဖန်တီးပါ + invalid: ဤဖိတ်ကြားချက်မှာ မမှန်ကန်ပါ invited_by: သင့်ကို ဖိတ်ခေါ်ထားသည် - max_uses: other: "%{count} အသုံးပြုမှုများ" From a3cbb4b2f1aff565582c280c6408d01cf0b54350 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 8 Dec 2023 08:54:20 -0500 Subject: [PATCH 48/73] Add spec for `CLI::Search#deploy` command (#28272) --- spec/lib/mastodon/cli/search_spec.rb | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/spec/lib/mastodon/cli/search_spec.rb b/spec/lib/mastodon/cli/search_spec.rb index 785dc2bd6..cb0c80c11 100644 --- a/spec/lib/mastodon/cli/search_spec.rb +++ b/spec/lib/mastodon/cli/search_spec.rb @@ -4,5 +4,79 @@ require 'rails_helper' require 'mastodon/cli/search' describe Mastodon::CLI::Search do + subject { cli.invoke(action, arguments, options) } + + let(:cli) { described_class.new } + let(:arguments) { [] } + let(:options) { {} } + it_behaves_like 'CLI Command' + + describe '#deploy' do + let(:action) { :deploy } + + context 'with concurrency out of range' do + let(:options) { { concurrency: -100 } } + + it 'Exits with error message' do + expect { subject } + .to output_results('this concurrency setting') + .and raise_error(SystemExit) + end + end + + context 'with batch size out of range' do + let(:options) { { batch_size: -100_000 } } + + it 'Exits with error message' do + expect { subject } + .to output_results('this batch_size setting') + .and raise_error(SystemExit) + end + end + + context 'without options' do + before { stub_search_indexes } + + let(:indexed_count) { 1 } + let(:deleted_count) { 2 } + + it 'reports about storage size' do + expect { subject } + .to output_results( + "Indexed #{described_class::INDICES.size * indexed_count} records", + "de-indexed #{described_class::INDICES.size * deleted_count}" + ) + end + end + + def stub_search_indexes + described_class::INDICES.each do |index| + allow(index) + .to receive_messages( + specification: instance_double(Chewy::Index::Specification, changed?: true, lock!: nil), + purge: nil + ) + + importer_double = importer_double_for(index) + allow(importer_double).to receive(:on_progress).and_yield([indexed_count, deleted_count]) + allow("Importer::#{index}Importer".constantize) + .to receive(:new) + .and_return(importer_double) + end + end + + def importer_double_for(index) + instance_double( + "Importer::#{index}Importer".constantize, + clean_up!: nil, + estimate!: 100, + import!: nil, + on_failure: nil, + # on_progress: nil, + optimize_for_import!: nil, + optimize_for_search!: nil + ) + end + end end From 490e4969a1a657cefbc9e4a9c11d5bffb5dc7a50 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Fri, 8 Dec 2023 08:54:48 -0500 Subject: [PATCH 49/73] Correct section naming in maintenance cli script (#28279) --- lib/mastodon/cli/maintenance.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index c53d74254..d0eff7da6 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -346,7 +346,7 @@ module Mastodon::CLI remove_index_if_exists!(:announcement_reactions, 'index_announcement_reactions_on_account_id_and_announcement_id') - say 'Removing duplicate account identity proofs…' + say 'Removing duplicate announcement reactions…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM announcement_reactions GROUP BY account_id, announcement_id, name HAVING count(*) > 1").each do |row| AnnouncementReaction.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end @@ -431,7 +431,7 @@ module Mastodon::CLI def deduplicate_domain_blocks! remove_index_if_exists!(:domain_blocks, 'index_domain_blocks_on_domain') - say 'Deduplicating domain_allows…' + say 'Deduplicating domain_blocks…' ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM domain_blocks GROUP BY domain HAVING count(*) > 1").each do |row| domain_blocks = DomainBlock.where(id: row['ids'].split(',')).by_severity.reverse.to_a @@ -462,7 +462,7 @@ module Mastodon::CLI UnavailableDomain.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy) end - say 'Restoring domain_allows indexes…' + say 'Restoring unavailable_domains indexes…' ActiveRecord::Base.connection.add_index :unavailable_domains, ['domain'], name: 'index_unavailable_domains_on_domain', unique: true end From afc8550b63434232bb06b196132f2a180516061b Mon Sep 17 00:00:00 2001 From: Michael Stanclift <mx@vmstan.com> Date: Mon, 11 Dec 2023 01:49:10 -0600 Subject: [PATCH 50/73] Change preview card deletes to be done using batch method (#28183) --- app/lib/vacuum/preview_cards_vacuum.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/lib/vacuum/preview_cards_vacuum.rb b/app/lib/vacuum/preview_cards_vacuum.rb index 14fdeda1c..9e34c87c3 100644 --- a/app/lib/vacuum/preview_cards_vacuum.rb +++ b/app/lib/vacuum/preview_cards_vacuum.rb @@ -14,9 +14,8 @@ class Vacuum::PreviewCardsVacuum private def vacuum_cached_images! - preview_cards_past_retention_period.find_each do |preview_card| - preview_card.image.destroy - preview_card.save + preview_cards_past_retention_period.find_in_batches do |preview_card| + AttachmentBatch.new(PreviewCard, preview_card).clear end end From bd88883b6e6c7c614eac3c97fc40783ea7b8c91b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:52:53 +0000 Subject: [PATCH 51/73] Update eslint (non-major) (#28313) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 128 +++++++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/yarn.lock b/yarn.lock index 45f58eaad..4e1d0bd8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1726,9 +1726,9 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.3": - version: 2.1.3 - resolution: "@eslint/eslintrc@npm:2.1.3" +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: ajv: "npm:^6.12.4" debug: "npm:^4.3.2" @@ -1739,14 +1739,14 @@ __metadata: js-yaml: "npm:^4.1.0" minimatch: "npm:^3.1.2" strip-json-comments: "npm:^3.1.1" - checksum: f4103f4346126292eb15581c5a1d12bef03410fd3719dedbdb92e1f7031d46a5a2d60de8566790445d5d4b70b75ba050876799a11f5fff8265a91ee3fa77dab0 + checksum: 32f67052b81768ae876c84569ffd562491ec5a5091b0c1e1ca1e0f3c24fb42f804952fdd0a137873bc64303ba368a71ba079a6f691cee25beee9722d94cc8573 languageName: node linkType: hard -"@eslint/js@npm:8.54.0": - version: 8.54.0 - resolution: "@eslint/js@npm:8.54.0" - checksum: d61fb4a0be6af2d8cb290121c329697664a75d6255a29926d5454fb02aeb02b87112f67fdf218d10abac42f90c570ac366126751baefc5405d0e017ed0c946c5 +"@eslint/js@npm:8.55.0": + version: 8.55.0 + resolution: "@eslint/js@npm:8.55.0" + checksum: 88ab9fc57a651becd2b32ec40a3958db27fae133b1ae77bebd733aa5bbd00a92f325bb02f20ad680d31c731fa49b22f060a4777dd52eb3e27da013d940bd978d languageName: node linkType: hard @@ -3682,14 +3682,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.0.0": - version: 6.11.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.11.0" + version: 6.13.2 + resolution: "@typescript-eslint/eslint-plugin@npm:6.13.2" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.11.0" - "@typescript-eslint/type-utils": "npm:6.11.0" - "@typescript-eslint/utils": "npm:6.11.0" - "@typescript-eslint/visitor-keys": "npm:6.11.0" + "@typescript-eslint/scope-manager": "npm:6.13.2" + "@typescript-eslint/type-utils": "npm:6.13.2" + "@typescript-eslint/utils": "npm:6.13.2" + "@typescript-eslint/visitor-keys": "npm:6.13.2" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -3702,44 +3702,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 6645aa09b9d51c5e3ea781eaf74da75b94f83f3e2d7b3dd988d5ce7eb82dd87e3509471cf2ee8c6b2428d907df5f1b02f29dbd04f54c2653f9566c8c4ce98009 + checksum: 531a4406d872738d165c6a66cb26e976523c94053b022a8210dc9fd10e91b79b705bc0fcc77145e9744e4108b53bdba55e02a10dc17757b22be92aff57849384 languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.0.0": - version: 6.11.0 - resolution: "@typescript-eslint/parser@npm:6.11.0" + version: 6.13.2 + resolution: "@typescript-eslint/parser@npm:6.13.2" dependencies: - "@typescript-eslint/scope-manager": "npm:6.11.0" - "@typescript-eslint/types": "npm:6.11.0" - "@typescript-eslint/typescript-estree": "npm:6.11.0" - "@typescript-eslint/visitor-keys": "npm:6.11.0" + "@typescript-eslint/scope-manager": "npm:6.13.2" + "@typescript-eslint/types": "npm:6.13.2" + "@typescript-eslint/typescript-estree": "npm:6.13.2" + "@typescript-eslint/visitor-keys": "npm:6.13.2" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: e7caeb20069102e21f468fc0dbe7ff6fb6b1efa9e72f4c9f39d4a865ed0633f39130b593ef9ae8f394ca1d70563e15410faf30a482a97809951eaac6ed3a67da + checksum: 2c62b8cd8a37eb2ea59cd00e559f51a9f57af746e2040e872af3c58ddd3f4071ad7b7009789bdeb0e0d4ee0343bfe96ee77288020f3ae22d08e1674203f5e156 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/scope-manager@npm:6.11.0" +"@typescript-eslint/scope-manager@npm:6.13.2": + version: 6.13.2 + resolution: "@typescript-eslint/scope-manager@npm:6.13.2" dependencies: - "@typescript-eslint/types": "npm:6.11.0" - "@typescript-eslint/visitor-keys": "npm:6.11.0" - checksum: d8999e2d1a4cbde8a79df5e3ec416f0e3db9532d39f2f4bb5a0ebdf954ae75c183d3277579ba05268fe2c88e88ef87f0fa12f02bb8d95d9e67d92e411241f3a3 + "@typescript-eslint/types": "npm:6.13.2" + "@typescript-eslint/visitor-keys": "npm:6.13.2" + checksum: 9b159e5bb10dfb5953e71488200b4126378fc7e987ce7d90946aea9ec40cd66c7ada92399657c5d9794189b764ca6f4eb38a8dcb9e4c5aa50ab6000a39636b9c languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/type-utils@npm:6.11.0" +"@typescript-eslint/type-utils@npm:6.13.2": + version: 6.13.2 + resolution: "@typescript-eslint/type-utils@npm:6.13.2" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.11.0" - "@typescript-eslint/utils": "npm:6.11.0" + "@typescript-eslint/typescript-estree": "npm:6.13.2" + "@typescript-eslint/utils": "npm:6.13.2" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -3747,23 +3747,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: ff68f2e052b8d688f1dc1a0050746704c8e0ab6263b47f1f52da73a7d251678e4950af23a95e1cd8e3fcea2457e6e5294ddbe01d29dafa2fdfb5b11ed9452a3f + checksum: 1ca97c78abdf479aea0c54e869fda2ae2f69de1974cc063062ce7b5b16c7fdf497ea15c50a29dd5941ea1b6b77e8f1213a5c272a747e334ac69ede083f327468 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/types@npm:6.11.0" - checksum: 23182813db39a5e9b9bcc1e85306c953f7b8b22d3885e41fcac0bd725c170fbcb70f4ce55633678cc5921dcf062fa0e55635eb39480c118a4411a00354820223 +"@typescript-eslint/types@npm:6.13.2": + version: 6.13.2 + resolution: "@typescript-eslint/types@npm:6.13.2" + checksum: 029918ca5b1442bb4bc435773504ce32191e2c3e2fde8d4176bb6513f03e3dfa2aa9724b2d22b1640656d666b97f7a7ebfeaf67b881d5e07250828fa83e3ebe8 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.11.0" +"@typescript-eslint/typescript-estree@npm:6.13.2": + version: 6.13.2 + resolution: "@typescript-eslint/typescript-estree@npm:6.13.2" dependencies: - "@typescript-eslint/types": "npm:6.11.0" - "@typescript-eslint/visitor-keys": "npm:6.11.0" + "@typescript-eslint/types": "npm:6.13.2" + "@typescript-eslint/visitor-keys": "npm:6.13.2" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -3772,34 +3772,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 3e183e554e1bc74f065da3015f7137eb40c262f989c547701b1e3f4f20134e574e56b749288cd00d77b9d1ddb705546613c2457661ffc63b6060ffa97ba3aac8 + checksum: 1c4c59dce0c51fdfee34d9f418e64fe28e3ec1a97661efc8a3d2780bdff36aff38de9090d356a968f394fa6d4e9c058936ce9cd260d4c44a52761ecd74915bce languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.11.0, @typescript-eslint/utils@npm:^6.5.0": - version: 6.11.0 - resolution: "@typescript-eslint/utils@npm:6.11.0" +"@typescript-eslint/utils@npm:6.13.2, @typescript-eslint/utils@npm:^6.5.0": + version: 6.13.2 + resolution: "@typescript-eslint/utils@npm:6.13.2" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.11.0" - "@typescript-eslint/types": "npm:6.11.0" - "@typescript-eslint/typescript-estree": "npm:6.11.0" + "@typescript-eslint/scope-manager": "npm:6.13.2" + "@typescript-eslint/types": "npm:6.13.2" + "@typescript-eslint/typescript-estree": "npm:6.13.2" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: c91eb4578607959acc2b43ddc791571682e45601a19b25d5d120786ed4af607656f83c5c1fa71972e549ddfb5542acf2f7d443ae93b32ee28192c22c106b8883 + checksum: 84969be91e7949868eaaa289288c9d71927f0e427b572501b0991d8d62b40a4234f7287c35b35d276ccbb53e9ea5457b8250fcf4941e60e6b9ba4065fbfba416 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.11.0" +"@typescript-eslint/visitor-keys@npm:6.13.2": + version: 6.13.2 + resolution: "@typescript-eslint/visitor-keys@npm:6.13.2" dependencies: - "@typescript-eslint/types": "npm:6.11.0" + "@typescript-eslint/types": "npm:6.13.2" eslint-visitor-keys: "npm:^3.4.1" - checksum: 5f48329422b7f286196661d39e93e9defd7c5cf80e6c84c8d03459853f5d9f86a5e91c5e80ea572dcdb907ebbe503bbcc77aeb8b468c294b2aa7b3ccfc81cb88 + checksum: c173bc1fcc42c3075a5ee094e7f3bf0279d98315c25ff49e20d02d79022b1d0402accfa113b070afb4d52a6f6d180594b67baa8b6a784eabdf82b54dd1ff454c languageName: node linkType: hard @@ -7313,13 +7313,13 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^9.0.0": - version: 9.0.0 - resolution: "eslint-config-prettier@npm:9.0.0" + version: 9.1.0 + resolution: "eslint-config-prettier@npm:9.1.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: bc1f661915845c631824178942e5d02f858fe6d0ea796f0050d63e0f681927b92696e81139dd04714c08c3e7de580fd079c66162e40070155ba79eaee78ab5d0 + checksum: 6d332694b36bc9ac6fdb18d3ca2f6ac42afa2ad61f0493e89226950a7091e38981b66bac2b47ba39d15b73fff2cd32c78b850a9cf9eed9ca9a96bfb2f3a2f10d languageName: node linkType: hard @@ -7555,13 +7555,13 @@ __metadata: linkType: hard "eslint@npm:^8.41.0": - version: 8.54.0 - resolution: "eslint@npm:8.54.0" + version: 8.55.0 + resolution: "eslint@npm:8.55.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.6.1" - "@eslint/eslintrc": "npm:^2.1.3" - "@eslint/js": "npm:8.54.0" + "@eslint/eslintrc": "npm:^2.1.4" + "@eslint/js": "npm:8.55.0" "@humanwhocodes/config-array": "npm:^0.11.13" "@humanwhocodes/module-importer": "npm:^1.0.1" "@nodelib/fs.walk": "npm:^1.2.8" @@ -7598,7 +7598,7 @@ __metadata: text-table: "npm:^0.2.0" bin: eslint: bin/eslint.js - checksum: 4f205f832bdbd0218cde374b067791f4f76d7abe8de86b2dc849c273899051126d912ebf71531ee49b8eeaa22cad77febdc8f2876698dc2a76e84a8cb976af22 + checksum: d28c0b60f19bb7d355cb8393e77b018c8f548dba3f820b799c89bb2e0c436ee26084e700c5e57e1e97e7972ec93065277849141b82e7b0c0d02c2dc1e553a2a1 languageName: node linkType: hard From d0e7999a904d6d5eb290f2378ce36809bfecb502 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:53:13 +0000 Subject: [PATCH 52/73] Update dependency typescript to v5.3.3 (#28312) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4e1d0bd8c..34e22f052 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16495,22 +16495,22 @@ __metadata: linkType: hard "typescript@npm:5, typescript@npm:^5.0.4": - version: 5.3.2 - resolution: "typescript@npm:5.3.2" + version: 5.3.3 + resolution: "typescript@npm:5.3.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: d7dbe1fbe19039e36a65468ea64b5d338c976550394ba576b7af9c68ed40c0bc5d12ecce390e4b94b287a09a71bd3229f19c2d5680611f35b7c53a3898791159 + checksum: e33cef99d82573624fc0f854a2980322714986bc35b9cb4d1ce736ed182aeab78e2cb32b385efa493b2a976ef52c53e20d6c6918312353a91850e2b76f1ea44f languageName: node linkType: hard "typescript@patch:typescript@npm%3A5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>": - version: 5.3.2 - resolution: "typescript@patch:typescript@npm%3A5.3.2#optional!builtin<compat/typescript>::version=5.3.2&hash=e012d7" + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin<compat/typescript>::version=5.3.3&hash=e012d7" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 73c8bad74e732d93211c9d77f28b03307e2f5fc6a0afc73f4b783261ab567686a16d6ae958bdaef383a00be1b0b8c8b6741dd6ca3d13af4963fa7e47456d49c7 + checksum: 1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500 languageName: node linkType: hard From 7ddd937330103cc9030ee375c3e62904b0b7508f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 08:53:32 +0100 Subject: [PATCH 53/73] Update dependency prettier to v3.1.1 (#28311) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 34e22f052..7a1524204 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13312,11 +13312,11 @@ __metadata: linkType: hard "prettier@npm:^3.0.0": - version: 3.1.0 - resolution: "prettier@npm:3.1.0" + version: 3.1.1 + resolution: "prettier@npm:3.1.1" bin: prettier: bin/prettier.cjs - checksum: a45ea70aa97fde162ea4c4aba3dfc7859aa6a732a1db34458d9535dc3c2c16d3bc3fb5689e6cd76aa835562555303b02d9449fd2e15af3b73c8053557e25c5b6 + checksum: facc944ba20e194ff4db765e830ffbcb642803381f0d2033ed397e79904fa4ccc877dc25ad68f42d36985c01d051c990ca1b905fb83d2d7d65fe69e4386fa1a3 languageName: node linkType: hard From a117155728319e36f3a5d2256de83b0985a40e0c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:54:01 +0000 Subject: [PATCH 54/73] Update DefinitelyTyped types (non-major) (#28310) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7a1524204..f246cf412 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3027,11 +3027,11 @@ __metadata: linkType: hard "@types/emoji-mart@npm:^3.0.9": - version: 3.0.13 - resolution: "@types/emoji-mart@npm:3.0.13" + version: 3.0.14 + resolution: "@types/emoji-mart@npm:3.0.14" dependencies: "@types/react": "npm:*" - checksum: 840f920c3242e1d274f0102e67cb2d00434e1fd370e0bcc8983b43b6b62322b01e3ddcd5fb078c60883e613530a7c70b8c40060624897543cd4da9441ca81486 + checksum: 23ded65fce9b3355fbe903d3971cb67cc827a5d587464bb7e3f349615527ef4a9197b3bb59fa84c4391d1b901e7f200f686a7fc83f649ae2a51a0fb948cbadfb languageName: node linkType: hard @@ -3175,12 +3175,12 @@ __metadata: linkType: hard "@types/jest@npm:^29.5.2": - version: 29.5.10 - resolution: "@types/jest@npm:29.5.10" + version: 29.5.11 + resolution: "@types/jest@npm:29.5.11" dependencies: expect: "npm:^29.0.0" pretty-format: "npm:^29.0.0" - checksum: b46171d59d12a5f69bbe710f65eaf59a8073337c6b4a67dff8158575caec53f1c61f8a7d645b34d6ac3c4ea398acd30f0c5d1c4a131c0c918798019264a3397d + checksum: 524a3394845214581278bf4d75055927261fbeac7e1a89cd621bd0636da37d265fe0a85eac58b5778758faad1cbd7c7c361dfc190c78ebde03a91cce33463261 languageName: node linkType: hard @@ -3374,11 +3374,11 @@ __metadata: linkType: hard "@types/react-helmet@npm:^6.1.6": - version: 6.1.9 - resolution: "@types/react-helmet@npm:6.1.9" + version: 6.1.11 + resolution: "@types/react-helmet@npm:6.1.11" dependencies: "@types/react": "npm:*" - checksum: d1823582903d6e70f1f447c7bec9e844b6f85f5de84cbcde5c8bbeecc064db1394c786ed9b9ded30544afe5c91e57c7e8105171df1643998f64c0aeab9f7f2aa + checksum: f7b3bb2151d992a108ae46fed876fb9c8119108397d9a01d150c5642782997542c8b3c52e742b56e8689b7dbfa62ca9cfc76aa7e05dec4e60c652f7ef53fa783 languageName: node linkType: hard @@ -3495,13 +3495,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": - version: 18.2.41 - resolution: "@types/react@npm:18.2.41" + version: 18.2.43 + resolution: "@types/react@npm:18.2.43" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 5cc72491ce8be95e7bbedd8bf039ca971772ecd22d989feb045af7e73247c7e6cff25a2f1c2200be461fb2f6b5aacef739e1ba9fd83c744209dfd3ce8aa75afe + checksum: 10477a50fbd3c0cc5b8a2ade679f442717f68fb27c8460b2aa1d3256cd18c48f742bbe5b9ee37a8c4c5f832ffa37b3a23c09fd96dd880a8e3182d8929c05e803 languageName: node linkType: hard From b82fc8a2ca29f03f821add41bea5221ceac9cafd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:54:22 +0000 Subject: [PATCH 55/73] Update dependency ws to v8.15.0 (#28308) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index f246cf412..0ef881126 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17738,8 +17738,8 @@ __metadata: linkType: hard "ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.14.2": - version: 8.14.2 - resolution: "ws@npm:8.14.2" + version: 8.15.0 + resolution: "ws@npm:8.15.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -17748,7 +17748,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 35b4c2da048b8015c797fd14bcb5a5766216ce65c8a5965616a5440ca7b6c3681ee3cbd0ea0c184a59975556e9d58f2002abf8485a14d11d3371770811050a16 + checksum: b778a405b2589ffbf549323e2f404f1f72e372a049d332d2f0b1f33057e9fbb14a05aa474cb156e4584b418cd95edf4297c0ca5263d6519e8009064bf8e0b80d languageName: node linkType: hard From 11d2bd97165bf68439a8d2293c48b1fcefc8930c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:55:07 -0500 Subject: [PATCH 56/73] Fix intermittent failure from unspecified order in `api/v1/accounts/relationships` spec (#28306) --- .../api/v1/accounts/relationships_spec.rb | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb index cea45168a..b06ce0509 100644 --- a/spec/requests/api/v1/accounts/relationships_spec.rb +++ b/spec/requests/api/v1/accounts/relationships_spec.rb @@ -31,8 +31,8 @@ describe 'GET /api/v1/accounts/relationships' do .to have_http_status(200) expect(body_as_json) .to be_an(Enumerable) - .and have_attributes( - first: include( + .and contain_exactly( + include( following: true, followed_by: false ) @@ -53,9 +53,11 @@ describe 'GET /api/v1/accounts/relationships' do expect(body_as_json) .to be_an(Enumerable) .and have_attributes( - size: 2, - first: include(simon_item), - second: include(lewis_item) + size: 2 + ) + .and contain_exactly( + include(simon_item), + include(lewis_item) ) end end @@ -71,10 +73,12 @@ describe 'GET /api/v1/accounts/relationships' do expect(body_as_json) .to be_an(Enumerable) .and have_attributes( - size: 3, - first: include(simon_item), - second: include(lewis_item), - third: include(bob_item) + size: 3 + ) + .and contain_exactly( + include(simon_item), + include(lewis_item), + include(bob_item) ) end end @@ -88,9 +92,11 @@ describe 'GET /api/v1/accounts/relationships' do expect(body_as_json) .to be_an(Enumerable) .and have_attributes( - size: 2, - first: include(simon_item), - second: include(lewis_item) + size: 2 + ) + .and contain_exactly( + include(simon_item), + include(lewis_item) ) end end @@ -116,7 +122,6 @@ describe 'GET /api/v1/accounts/relationships' do muting: false, requested: false, domain_blocking: false, - } end @@ -129,7 +134,6 @@ describe 'GET /api/v1/accounts/relationships' do muting: false, requested: false, domain_blocking: false, - } end end @@ -149,8 +153,10 @@ describe 'GET /api/v1/accounts/relationships' do expect(body_as_json) .to be_an(Enumerable) .and have_attributes( - size: 1, - first: include( + size: 1 + ) + .and contain_exactly( + include( following: true, showing_reblogs: true ) @@ -168,8 +174,8 @@ describe 'GET /api/v1/accounts/relationships' do expect(body_as_json) .to be_an(Enumerable) - .and have_attributes( - first: include( + .and contain_exactly( + include( following: false, showing_reblogs: false ) From 78347d25567b1b8cf2cb9e1fd4cab002fd6aff59 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:55:45 -0500 Subject: [PATCH 57/73] Controller spec to request spec: `api/v1/accounts/familiar_followers` (#28305) --- .../v1/accounts/familiar_followers_spec.rb} | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) rename spec/{controllers/api/v1/accounts/familiar_followers_controller_spec.rb => requests/api/v1/accounts/familiar_followers_spec.rb} (53%) diff --git a/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb b/spec/requests/api/v1/accounts/familiar_followers_spec.rb similarity index 53% rename from spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb rename to spec/requests/api/v1/accounts/familiar_followers_spec.rb index 3c7c7e8b8..fdc0a3a93 100644 --- a/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb +++ b/spec/requests/api/v1/accounts/familiar_followers_spec.rb @@ -2,20 +2,16 @@ require 'rails_helper' -describe Api::V1::Accounts::FamiliarFollowersController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') } +describe 'Accounts Familiar Followers API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:follows' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } let(:account) { Fabricate(:account) } - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do + describe 'GET /api/v1/accounts/familiar_followers' do it 'returns http success' do - get :index, params: { account_id: account.id, limit: 2 } + get '/api/v1/accounts/familiar_followers', params: { account_id: account.id, limit: 2 }, headers: headers expect(response).to have_http_status(200) end @@ -26,7 +22,7 @@ describe Api::V1::Accounts::FamiliarFollowersController do it 'removes duplicate account IDs from params' do account_ids = [account_a, account_b, account_b, account_a, account_a].map { |a| a.id.to_s } - get :index, params: { id: account_ids } + get '/api/v1/accounts/familiar_followers', params: { id: account_ids }, headers: headers expect(body_as_json.pluck(:id)).to contain_exactly(account_a.id.to_s, account_b.id.to_s) end From 8f94502e7d092584c7bd723ae3e866c0aae2c95b Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:56:13 -0500 Subject: [PATCH 58/73] Controller spec to request spec: `api/v1/accounts/identify_proofs` (#28304) --- .../identity_proofs_controller_spec.rb | 23 ------------------- .../api/v1/accounts/identity_proofs_spec.rb | 19 +++++++++++++++ 2 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb create mode 100644 spec/requests/api/v1/accounts/identity_proofs_spec.rb diff --git a/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb b/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb deleted file mode 100644 index 6351de761..000000000 --- a/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Accounts::IdentityProofsController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') } - let(:account) { Fabricate(:account) } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - it 'returns http success' do - get :index, params: { account_id: account.id, limit: 2 } - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/accounts/identity_proofs_spec.rb b/spec/requests/api/v1/accounts/identity_proofs_spec.rb new file mode 100644 index 000000000..3727af7e8 --- /dev/null +++ b/spec/requests/api/v1/accounts/identity_proofs_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Accounts Identity Proofs API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:account) { Fabricate(:account) } + + describe 'GET /api/v1/accounts/identity_proofs' do + it 'returns http success' do + get "/api/v1/accounts/#{account.id}/identity_proofs", params: { limit: 2 }, headers: headers + + expect(response).to have_http_status(200) + end + end +end From a968898dc7d7a9c6ddea850a40d4f2fc9e7b446f Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:56:47 -0500 Subject: [PATCH 59/73] Controller spec to request spec: `api/v1/accounts/lists` (#28303) --- .../api/v1/accounts/lists_controller_spec.rb | 25 ------------------- spec/requests/api/v1/accounts/lists_spec.rb | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) delete mode 100644 spec/controllers/api/v1/accounts/lists_controller_spec.rb create mode 100644 spec/requests/api/v1/accounts/lists_spec.rb diff --git a/spec/controllers/api/v1/accounts/lists_controller_spec.rb b/spec/controllers/api/v1/accounts/lists_controller_spec.rb deleted file mode 100644 index 418839cfa..000000000 --- a/spec/controllers/api/v1/accounts/lists_controller_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Accounts::ListsController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') } - let(:account) { Fabricate(:account) } - let(:list) { Fabricate(:list, account: user.account) } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - user.account.follow!(account) - list.accounts << account - end - - describe 'GET #index' do - it 'returns http success' do - get :index, params: { account_id: account.id } - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/accounts/lists_spec.rb b/spec/requests/api/v1/accounts/lists_spec.rb new file mode 100644 index 000000000..48c0337e5 --- /dev/null +++ b/spec/requests/api/v1/accounts/lists_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Accounts Lists API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:lists' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:account) { Fabricate(:account) } + let(:list) { Fabricate(:list, account: user.account) } + + before do + user.account.follow!(account) + list.accounts << account + end + + describe 'GET /api/v1/accounts/lists' do + it 'returns http success' do + get "/api/v1/accounts/#{account.id}/lists", headers: headers + + expect(response).to have_http_status(200) + end + end +end From e544b6df42a5ed8c2365f0a95d7e3e94c53c61f9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:57:33 -0500 Subject: [PATCH 60/73] Controller spec to request spec: `api/v1/accounts/lookup` (#28302) --- .../api/v1/accounts/lookup_controller_spec.rb | 23 ------------------- spec/requests/api/v1/accounts/lookup_spec.rb | 19 +++++++++++++++ 2 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 spec/controllers/api/v1/accounts/lookup_controller_spec.rb create mode 100644 spec/requests/api/v1/accounts/lookup_spec.rb diff --git a/spec/controllers/api/v1/accounts/lookup_controller_spec.rb b/spec/controllers/api/v1/accounts/lookup_controller_spec.rb deleted file mode 100644 index 37407766f..000000000 --- a/spec/controllers/api/v1/accounts/lookup_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::Accounts::LookupController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') } - let(:account) { Fabricate(:account) } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #show' do - it 'returns http success' do - get :show, params: { account_id: account.id, acct: account.acct } - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/accounts/lookup_spec.rb b/spec/requests/api/v1/accounts/lookup_spec.rb new file mode 100644 index 000000000..4c022c7c1 --- /dev/null +++ b/spec/requests/api/v1/accounts/lookup_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Accounts Lookup API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:account) { Fabricate(:account) } + + describe 'GET /api/v1/accounts/lookup' do + it 'returns http success' do + get '/api/v1/accounts/lookup', params: { account_id: account.id, acct: account.acct }, headers: headers + + expect(response).to have_http_status(200) + end + end +end From 94cc707ab398339dfe74472300056876fac81856 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:58:48 -0500 Subject: [PATCH 61/73] Controller spec to request spec: `api/v1/accounts/notes` (#28301) --- .../api/v1/accounts/notes_spec.rb} | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) rename spec/{controllers/api/v1/accounts/notes_controller_spec.rb => requests/api/v1/accounts/notes_spec.rb} (66%) diff --git a/spec/controllers/api/v1/accounts/notes_controller_spec.rb b/spec/requests/api/v1/accounts/notes_spec.rb similarity index 66% rename from spec/controllers/api/v1/accounts/notes_controller_spec.rb rename to spec/requests/api/v1/accounts/notes_spec.rb index 75599b32b..4f3ac68c7 100644 --- a/spec/controllers/api/v1/accounts/notes_controller_spec.rb +++ b/spec/requests/api/v1/accounts/notes_spec.rb @@ -2,21 +2,17 @@ require 'rails_helper' -describe Api::V1::Accounts::NotesController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:accounts') } +describe 'Accounts Notes API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'write:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } let(:account) { Fabricate(:account) } let(:comment) { 'foo' } - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'POST #create' do + describe 'POST /api/v1/accounts/:account_id/note' do subject do - post :create, params: { account_id: account.id, comment: comment } + post "/api/v1/accounts/#{account.id}/note", params: { comment: comment }, headers: headers end context 'when account note has reasonable length', :aggregate_failures do From 809506bdd40871ae8c56fa755d8d61bc279095a0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 02:59:40 -0500 Subject: [PATCH 62/73] Controller spec to request spec: `api/v1/accounts/pins` (#28300) --- .../api/v1/accounts/pins_controller_spec.rb | 40 ------------------ spec/requests/api/v1/accounts/pins_spec.rb | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 40 deletions(-) delete mode 100644 spec/controllers/api/v1/accounts/pins_controller_spec.rb create mode 100644 spec/requests/api/v1/accounts/pins_spec.rb diff --git a/spec/controllers/api/v1/accounts/pins_controller_spec.rb b/spec/controllers/api/v1/accounts/pins_controller_spec.rb deleted file mode 100644 index 36f525e75..000000000 --- a/spec/controllers/api/v1/accounts/pins_controller_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::Accounts::PinsController do - let(:john) { Fabricate(:user) } - let(:kevin) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: john.id, scopes: 'write:accounts') } - - before do - kevin.account.followers << john.account - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'POST #create' do - subject { post :create, params: { account_id: kevin.account.id } } - - it 'creates account_pin', :aggregate_failures do - expect do - subject - end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(1) - expect(response).to have_http_status(200) - end - end - - describe 'DELETE #destroy' do - subject { delete :destroy, params: { account_id: kevin.account.id } } - - before do - Fabricate(:account_pin, account: john.account, target_account: kevin.account) - end - - it 'destroys account_pin', :aggregate_failures do - expect do - subject - end.to change { AccountPin.where(account: john.account, target_account: kevin.account).count }.by(-1) - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/accounts/pins_spec.rb b/spec/requests/api/v1/accounts/pins_spec.rb new file mode 100644 index 000000000..c293715f7 --- /dev/null +++ b/spec/requests/api/v1/accounts/pins_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Accounts Pins API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'write:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:kevin) { Fabricate(:user) } + + before do + kevin.account.followers << user.account + end + + describe 'POST /api/v1/accounts/:account_id/pin' do + subject { post "/api/v1/accounts/#{kevin.account.id}/pin", headers: headers } + + it 'creates account_pin', :aggregate_failures do + expect do + subject + end.to change { AccountPin.where(account: user.account, target_account: kevin.account).count }.by(1) + expect(response).to have_http_status(200) + end + end + + describe 'POST /api/v1/accounts/:account_id/unpin' do + subject { post "/api/v1/accounts/#{kevin.account.id}/unpin", headers: headers } + + before do + Fabricate(:account_pin, account: user.account, target_account: kevin.account) + end + + it 'destroys account_pin', :aggregate_failures do + expect do + subject + end.to change { AccountPin.where(account: user.account, target_account: kevin.account).count }.by(-1) + expect(response).to have_http_status(200) + end + end +end From 16ede59d0a01759a32e3abb6a352a3c397bd982c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 03:00:41 -0500 Subject: [PATCH 63/73] Controller spec to request spec: `api/v1/featured_tags/suggestions` (#28298) --- .../suggestions_controller_spec.rb | 23 ------------------- .../api/v1/featured_tags/suggestions_spec.rb | 19 +++++++++++++++ 2 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb create mode 100644 spec/requests/api/v1/featured_tags/suggestions_spec.rb diff --git a/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb b/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb deleted file mode 100644 index 54c63dcc6..000000000 --- a/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V1::FeaturedTags::SuggestionsController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') } - let(:account) { Fabricate(:account) } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - it 'returns http success' do - get :index, params: { account_id: account.id, limit: 2 } - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/featured_tags/suggestions_spec.rb b/spec/requests/api/v1/featured_tags/suggestions_spec.rb new file mode 100644 index 000000000..f7b453b74 --- /dev/null +++ b/spec/requests/api/v1/featured_tags/suggestions_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Featured Tags Suggestions API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + let(:account) { Fabricate(:account) } + + describe 'GET /api/v1/featured_tags/suggestions' do + it 'returns http success' do + get '/api/v1/featured_tags/suggestions', params: { account_id: account.id, limit: 2 }, headers: headers + + expect(response).to have_http_status(200) + end + end +end From f5d6143aa1eba03391841d6df8ed80e09a4b5e05 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:01:17 +0100 Subject: [PATCH 64/73] Update dependency addressable to v2.8.6 (#28296) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7a7fdb01c..738bc7b8b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -118,7 +118,7 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) android_key_attestation (0.3.0) From 253393f3a8281dcf5850aabd917da727518f49c4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 03:04:45 -0500 Subject: [PATCH 65/73] Only attempt to remove indexes that exist in `CLI::Maintenance` script (#28286) --- lib/mastodon/cli/maintenance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index d0eff7da6..553ca056d 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -712,7 +712,7 @@ module Mastodon::CLI end def remove_index_if_exists!(table, name) - ActiveRecord::Base.connection.remove_index(table, name: name) + ActiveRecord::Base.connection.remove_index(table, name: name) if ActiveRecord::Base.connection.index_name_exists?(table, name) rescue ArgumentError, ActiveRecord::StatementInvalid nil end From 0c640925009a8dbfb8a2be9e4b45d3e40f727a52 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 03:13:28 -0500 Subject: [PATCH 66/73] Controller spec to request spec: `api/v1/accounts/search` (#28299) --- .../api/v1/accounts/search_controller_spec.rb | 22 ------------------- spec/requests/api/v1/accounts/search_spec.rb | 18 +++++++++++++++ 2 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 spec/controllers/api/v1/accounts/search_controller_spec.rb create mode 100644 spec/requests/api/v1/accounts/search_spec.rb diff --git a/spec/controllers/api/v1/accounts/search_controller_spec.rb b/spec/controllers/api/v1/accounts/search_controller_spec.rb deleted file mode 100644 index aa9455a4a..000000000 --- a/spec/controllers/api/v1/accounts/search_controller_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::Accounts::SearchController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #show' do - it 'returns http success' do - get :show, params: { q: 'query' } - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v1/accounts/search_spec.rb b/spec/requests/api/v1/accounts/search_spec.rb new file mode 100644 index 000000000..76b32e7b2 --- /dev/null +++ b/spec/requests/api/v1/accounts/search_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Accounts Search API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v1/accounts/search' do + it 'returns http success' do + get '/api/v1/accounts/search', params: { q: 'query' }, headers: headers + + expect(response).to have_http_status(200) + end + end +end From 0e4233de9d8a0912b0d837e32043c325dfc2e0ff Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 03:13:57 -0500 Subject: [PATCH 67/73] Controller spec to request spec: `api/v2/suggestions` (#28297) --- .../api/v2/suggestions_controller_spec.rb | 22 ------------------- spec/requests/api/v2/suggestions_spec.rb | 18 +++++++++++++++ 2 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 spec/controllers/api/v2/suggestions_controller_spec.rb create mode 100644 spec/requests/api/v2/suggestions_spec.rb diff --git a/spec/controllers/api/v2/suggestions_controller_spec.rb b/spec/controllers/api/v2/suggestions_controller_spec.rb deleted file mode 100644 index 5e6508bfd..000000000 --- a/spec/controllers/api/v2/suggestions_controller_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -describe Api::V2::SuggestionsController do - render_views - - let(:user) { Fabricate(:user) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - it 'returns http success' do - get :index - - expect(response).to have_http_status(200) - end - end -end diff --git a/spec/requests/api/v2/suggestions_spec.rb b/spec/requests/api/v2/suggestions_spec.rb new file mode 100644 index 000000000..5f1c97b8a --- /dev/null +++ b/spec/requests/api/v2/suggestions_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'Suggestions API' do + let(:user) { Fabricate(:user) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:scopes) { 'read' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'GET /api/v2/suggestions' do + it 'returns http success' do + get '/api/v2/suggestions', headers: headers + + expect(response).to have_http_status(200) + end + end +end From 79a81da69c86276fe17143954f06f6e6b58356d4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:22:52 +0100 Subject: [PATCH 68/73] New Crowdin Translations (automated) (#28291) Co-authored-by: GitHub Actions <noreply@github.com> --- app/javascript/mastodon/locales/en-GB.json | 7 +++ app/javascript/mastodon/locales/eo.json | 50 ++++++++++++++++++++ app/javascript/mastodon/locales/ko.json | 2 +- app/javascript/mastodon/locales/la.json | 16 +++---- app/javascript/mastodon/locales/ne.json | 54 +++++++++++++++++++++- app/javascript/mastodon/locales/sk.json | 1 + config/locales/activerecord.hr.yml | 15 ++++++ config/locales/activerecord.ru.yml | 2 +- config/locales/en-GB.yml | 16 +++++++ config/locales/sk.yml | 4 ++ 10 files changed, 156 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 7745311be..1a67fecb6 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -21,6 +21,7 @@ "account.blocked": "Blocked", "account.browse_more_on_origin_server": "Browse more on the original profile", "account.cancel_follow_request": "Cancel follow", + "account.copy": "Copy link to profile", "account.direct": "Privately mention @{name}", "account.disable_notifications": "Stop notifying me when @{name} posts", "account.domain_blocked": "Domain blocked", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Mark as read", "conversation.open": "View conversation", "conversation.with": "With {names}", + "copy_icon_button.copied": "Copied to clipboard", "copypaste.copied": "Copied", "copypaste.copy_to_clipboard": "Copy to clipboard", "directory.federated": "From known fediverse", @@ -222,6 +224,7 @@ "emoji_button.search_results": "Search results", "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", + "empty_column.account_hides_collections": "This user has chosen to not make this information available", "empty_column.account_suspended": "Account suspended", "empty_column.account_timeline": "No posts here!", "empty_column.account_unavailable": "Profile unavailable", @@ -478,6 +481,8 @@ "onboarding.follows.empty": "Unfortunately, no results can be shown right now. You can try using search or browsing the explore page to find people to follow, or try again later.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Personalize your home feed", + "onboarding.profile.discoverable": "Make my profile discoverable", + "onboarding.profile.discoverable_hint": "When you opt in to discoverability on Mastodon, your posts may appear in search results and trending, and your profile may be suggested to people with similar interests to you.", "onboarding.profile.display_name": "Display name", "onboarding.profile.display_name_hint": "Your full name or your fun name…", "onboarding.profile.lead": "You can always complete this later in the settings, where even more customisation options are available.", @@ -530,6 +535,7 @@ "privacy.unlisted.short": "Unlisted", "privacy_policy.last_updated": "Last updated {date}", "privacy_policy.title": "Privacy Policy", + "recommended": "Recommended", "refresh": "Refresh", "regeneration_indicator.label": "Loading…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", @@ -600,6 +606,7 @@ "search.quick_action.status_search": "Posts matching {x}", "search.search_or_paste": "Search or paste URL", "search_popout.full_text_search_disabled_message": "Unavailable on {domain}.", + "search_popout.full_text_search_logged_out_message": "Only available when logged in.", "search_popout.language_code": "ISO language code", "search_popout.options": "Search options", "search_popout.quick_actions": "Quick actions", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 537b8d0af..2678d83a5 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -21,6 +21,7 @@ "account.blocked": "Blokita", "account.browse_more_on_origin_server": "Foliumi pli ĉe la originala profilo", "account.cancel_follow_request": "Nuligi peton por sekvado", + "account.copy": "Kopii ligilon al profilo", "account.direct": "Private mencii @{name}", "account.disable_notifications": "Ne plu sciigi min, kiam @{name} mesaĝas", "account.domain_blocked": "Domajno blokita", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "Marki legita", "conversation.open": "Vidi konversacion", "conversation.with": "Kun {names}", + "copy_icon_button.copied": "Kopiis al kliptabulo", "copypaste.copied": "Kopiita", "copypaste.copy_to_clipboard": "Kopii al dosierujo", "directory.federated": "El konata fediverso", @@ -202,7 +204,9 @@ "dismissable_banner.community_timeline": "Jen la plej novaj publikaj afiŝoj de uzantoj, kies kontojn gastigas {domain}.", "dismissable_banner.dismiss": "Eksigi", "dismissable_banner.explore_links": "Tiuj novaĵoj estas aktuale priparolataj de uzantoj en tiu ĉi kaj aliaj serviloj, sur la malcentrigita reto.", + "dismissable_banner.explore_statuses": "Ĉi tioj estas afiŝoj de socia reto kiu populariĝas hodiau.", "dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.", + "dismissable_banner.public_timeline": "Ĉi tioj estas plej lastaj publikaj afiŝoj de personoj ĉe socia reto kiu personoj ĉe {domain} sekvas.", "embed.instructions": "Enkorpigu ĉi tiun afiŝon en vian retejon per kopio de la suba kodo.", "embed.preview": "Ĝi aperos tiel:", "emoji_button.activity": "Agadoj", @@ -220,6 +224,7 @@ "emoji_button.search_results": "Serĉaj rezultoj", "emoji_button.symbols": "Simboloj", "emoji_button.travel": "Vojaĝoj kaj lokoj", + "empty_column.account_hides_collections": "Ĉi tiu uzanto elektis ne disponebligi ĉi tiu informon", "empty_column.account_suspended": "Konto suspendita", "empty_column.account_timeline": "Neniu afiŝo ĉi tie!", "empty_column.account_unavailable": "Profilo ne disponebla", @@ -229,6 +234,8 @@ "empty_column.direct": "Vi ankoraŭ ne havas privatan mencion. Kiam vi sendos aŭ ricevos iun, tiu aperos ĉi tie.", "empty_column.domain_blocks": "Ankoraŭ neniu domajno estas blokita.", "empty_column.explore_statuses": "Nenio tendencas nun. Rekontrolu poste!", + "empty_column.favourited_statuses": "Vi ankoraŭ ne havas stelumitan afiŝon.", + "empty_column.favourites": "Ankoraŭ neniu stelumis tiun afiŝon.", "empty_column.follow_requests": "Vi ne ankoraŭ havas iun peton de sekvado. Kiam vi ricevos unu, ĝi aperos ĉi tie.", "empty_column.followed_tags": "Vi ankoraŭ ne sekvas iujn kradvortojn. Kiam vi faras, ili aperos ĉi tie.", "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.", @@ -292,19 +299,36 @@ "hashtag.column_settings.tag_mode.any": "Iu ajn", "hashtag.column_settings.tag_mode.none": "Neniu", "hashtag.column_settings.tag_toggle": "Aldoni pliajn etikedojn por ĉi tiu kolumno", + "hashtag.counter_by_accounts": "{count, plural,one {{counter} partoprenanto} other {{counter} partoprenantoj}}", + "hashtag.counter_by_uses": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}}", + "hashtag.counter_by_uses_today": "{count, plural,one {{counter} afiŝo} other {{counter} afiŝoj}} hodiau", "hashtag.follow": "Sekvi la kradvorton", "hashtag.unfollow": "Ne plu sekvi la kradvorton", + "hashtags.and_other": "…kaj {count, plural,other {# pli}}", + "home.actions.go_to_explore": "Vidi kio populariĝas", "home.actions.go_to_suggestions": "Trovi homojn por sekvi", "home.column_settings.basic": "Bazaj agordoj", "home.column_settings.show_reblogs": "Montri diskonigojn", "home.column_settings.show_replies": "Montri respondojn", + "home.explore_prompt.body": "Via hejmafiŝaro havos miksitajn afiŝojn de kradvortoj kiujn vi elektis sekvi, personoj kiujn vi elektis sekvi, kaj afiŝoj kiujn ili suprenigis.", + "home.explore_prompt.title": "Ĉi tio estas via hejma paĝo en Mastodon.", "home.hide_announcements": "Kaŝi la anoncojn", + "home.pending_critical_update.body": "Ĝisdatigu vian servilon de Mastodon kiel eble plej baldau!", + "home.pending_critical_update.link": "Vidi ĝisdatigojn", + "home.pending_critical_update.title": "Kritika sekurĝisdatigo estas disponebla!", "home.show_announcements": "Montri anoncojn", + "interaction_modal.description.favourite": "Per konto ĉe Mastodon, vi povas stelumiti ĉi tiun afiŝon por sciigi la afiŝanton ke vi aprezigas ŝin kaj konservas por la estonteco.", "interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povos sekvi {name} por vidi ties mesaĝojn en via hejmo.", "interaction_modal.description.reblog": "Kun konto ĉe Mastodon, vi povas diskonigi ĉi tiun afiŝon, por ke viaj propraj sekvantoj vidu ĝin.", "interaction_modal.description.reply": "Kun konto ĉe Mastodon, vi povos respondi al ĉi tiu mesaĝo.", + "interaction_modal.login.action": "Prenu min hejmen", + "interaction_modal.login.prompt": "Domajno de via hejma servilo, ekz. mastodon.social", + "interaction_modal.no_account_yet": "Ĉu ne estas ĉe Mastodon?", "interaction_modal.on_another_server": "En alia servilo", "interaction_modal.on_this_server": "En ĉi tiu servilo", + "interaction_modal.sign_in": "Vi ne estas ensalutita al ĉi tiu servilo.", + "interaction_modal.sign_in_hint": "Gvideto: Tio estas la retejo kie vi registris. Vi ankau povas tajpi vian plenan uzantonomon!", + "interaction_modal.title.favourite": "Stelumi la afiŝon de {name}", "interaction_modal.title.follow": "Sekvi {name}", "interaction_modal.title.reblog": "Akceli la afiŝon de {name}", "interaction_modal.title.reply": "Respondi al la afiŝo de {name}", @@ -320,6 +344,8 @@ "keyboard_shortcuts.direct": "por malfermi la kolumnon pri privataj mencioj", "keyboard_shortcuts.down": "iri suben en la listo", "keyboard_shortcuts.enter": "malfermi mesaĝon", + "keyboard_shortcuts.favourite": "Stelumi afiŝon", + "keyboard_shortcuts.favourites": "Malfermi la liston de la stelumoj", "keyboard_shortcuts.federated": "Malfermi la frataran templinion", "keyboard_shortcuts.heading": "Klavaraj mallongigoj", "keyboard_shortcuts.home": "Malfermi la hejman templinion", @@ -366,6 +392,7 @@ "lists.search": "Serĉi inter la homoj, kiujn vi sekvas", "lists.subheading": "Viaj listoj", "load_pending": "{count,plural, one {# nova elemento} other {# novaj elementoj}}", + "loading_indicator.label": "Ŝargado…", "media_gallery.toggle_visible": "{number, plural, one {Kaŝi la bildon} other {Kaŝi la bildojn}}", "moved_to_account_banner.text": "Via konto {disabledAccount} estas malvalidigita ĉar vi movis ĝin al {movedToAccount}.", "mute_modal.duration": "Daŭro", @@ -382,6 +409,7 @@ "navigation_bar.domain_blocks": "Blokitaj domajnoj", "navigation_bar.edit_profile": "Redakti profilon", "navigation_bar.explore": "Esplori", + "navigation_bar.favourites": "Stelumoj", "navigation_bar.filters": "Silentigitaj vortoj", "navigation_bar.follow_requests": "Petoj de sekvado", "navigation_bar.followed_tags": "Sekvataj kradvortoj", @@ -389,6 +417,7 @@ "navigation_bar.lists": "Listoj", "navigation_bar.logout": "Adiaŭi", "navigation_bar.mutes": "Silentigitaj uzantoj", + "navigation_bar.opened_in_classic_interface": "Afiŝoj, kontoj, kaj aliaj specifaj paĝoj kiuj estas malfermititaj defaulta en la klasika reta interfaco.", "navigation_bar.personal": "Persone", "navigation_bar.pins": "Alpinglitaj mesaĝoj", "navigation_bar.preferences": "Preferoj", @@ -398,6 +427,7 @@ "not_signed_in_indicator.not_signed_in": "Necesas saluti por aliri tiun rimedon.", "notification.admin.report": "{name} raportis {target}", "notification.admin.sign_up": "{name} kreis konton", + "notification.favourite": "{name} stelumis vian afiŝon", "notification.follow": "{name} eksekvis vin", "notification.follow_request": "{name} petis sekvi vin", "notification.mention": "{name} menciis vin", @@ -411,6 +441,7 @@ "notifications.column_settings.admin.report": "Novaj raportoj:", "notifications.column_settings.admin.sign_up": "Novaj registriĝoj:", "notifications.column_settings.alert": "Sciigoj de la retumilo", + "notifications.column_settings.favourite": "Stelumoj:", "notifications.column_settings.filter_bar.advanced": "Montri ĉiujn kategoriojn", "notifications.column_settings.filter_bar.category": "Rapida filtra breto", "notifications.column_settings.filter_bar.show_bar": "Montri la breton de filtrilo", @@ -428,6 +459,7 @@ "notifications.column_settings.update": "Redaktoj:", "notifications.filter.all": "Ĉiuj", "notifications.filter.boosts": "Diskonigoj", + "notifications.filter.favourites": "Stelumoj", "notifications.filter.follows": "Sekvoj", "notifications.filter.mentions": "Mencioj", "notifications.filter.polls": "Balotenketaj rezultoj", @@ -441,14 +473,29 @@ "notifications_permission_banner.enable": "Ŝalti retumilajn sciigojn", "notifications_permission_banner.how_to_control": "Por ricevi sciigojn kiam Mastodon ne estas malfermita, ebligu labortablajn sciigojn. Vi povas regi precize kiuj specoj de interagoj generas labortablajn sciigojn per la supra butono {icon} post kiam ili estas ebligitaj.", "notifications_permission_banner.title": "Neniam preterlasas iun ajn", + "onboarding.action.back": "Prenu min reen", + "onboarding.actions.back": "Prenu min reen", "onboarding.actions.go_to_explore": "See what's trending", "onboarding.actions.go_to_home": "Go to your home feed", "onboarding.compose.template": "Saluton #Mastodon!", "onboarding.follows.empty": "Bedaŭrinde, neniu rezulto estas montrebla nuntempe. Vi povas provi serĉi aŭ foliumi la esploran paĝon por trovi kontojn por sekvi, aŭ retrovi baldaŭ.", "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", "onboarding.follows.title": "Popular on Mastodon", + "onboarding.profile.discoverable": "Trovebligi mian profilon", + "onboarding.profile.discoverable_hint": "Kiam vi aliĝi al trovebleco ĉe Mastodon, viaj afiŝoj eble aperos en serĉaj rezultoj kaj populariĝoj, kaj via profilo eble estas sugestota al personoj kun similaj intereseoj al vi.", + "onboarding.profile.display_name": "Publika nomo", + "onboarding.profile.display_name_hint": "Via plena nomo aŭ via kromnomo…", + "onboarding.profile.lead": "Vi ĉiam povas plenigi ĉi tion poste en la agordoj, kie eĉ pli da personecigagordoj estas disponeblaj.", + "onboarding.profile.note": "Sinprezento", + "onboarding.profile.note_hint": "Vi povas @mencii aliajn homojn aŭ #kradvortojn…", "onboarding.profile.save_and_continue": "Konservi kaj daŭrigi", + "onboarding.profile.title": "Profila fikso", + "onboarding.profile.upload_avatar": "Alŝuti profilbildon", + "onboarding.profile.upload_header": "Alŝuti profilkapbildon", + "onboarding.share.lead": "Sciigi personojn pri kiel ili povas trovi vin ĉe Mastodon!", "onboarding.share.message": "Mi estas {username} en #Mastodon! Sekvu min ĉe {url}", + "onboarding.share.next_steps": "Eblaj malantauaj paŝoj:", + "onboarding.share.title": "Disvastigi vian profilon", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.skip": "Want to skip right ahead?", "onboarding.start.title": "Vi atingas ĝin!", @@ -460,6 +507,9 @@ "onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.title": "Share your profile", + "onboarding.tips.2fa": "<strong>Ĉu vi scias?</strong> Vi povas sekurigi vian konton per efektivigi dufaktora autentigo en via kontoagordoj.", + "onboarding.tips.accounts_from_other_servers": "<strong>Ĉu vi scias?</strong> Ĉar Mastodon estas sencentra, kelkaj profiloj kiujn vi trovi estas gastigitaj ĉe aliaj serviloj kiuj ne estas via.", + "onboarding.tips.migration": "<strong>Ĉu vi scias?</strong> Se vi sentas ke {domain} ne estas bona servilelekto por vi en la estonteco, vi povas translokiĝi al alia servilo de Mastodon sen malgajni viajn sekvantojn.", "password_confirmation.mismatching": "Pasvorto konfirmo ne kongruas", "picture_in_picture.restore": "Remetu ĝin", "poll.closed": "Finita", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 5b76cd67c..4606916c1 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -239,7 +239,7 @@ "empty_column.follow_requests": "아직 팔로우 요청이 없습니다. 요청을 받았을 때 여기에 나타납니다.", "empty_column.followed_tags": "아직 아무 해시태그도 팔로우하고 있지 않습니다. 해시태그를 팔로우하면, 여기에 표시됩니다.", "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.", - "empty_column.home": "당신의 홈 타임라인은 비어있습니다! 더 많은 사람들을 팔로우 하여 채워보세요. {suggestions}", + "empty_column.home": "당신의 홈 타임라인은 비어있습니다! 더 많은 사람을 팔로우하여 채워보세요. {suggestions}", "empty_column.list": "리스트에 아직 아무것도 없습니다. 리스트의 누군가가 게시물을 올리면 여기에 나타납니다.", "empty_column.lists": "아직 리스트가 없습니다. 리스트를 만들면 여기에 나타납니다.", "empty_column.mutes": "아직 아무도 뮤트하지 않았습니다.", diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json index e4bd9365a..3e5747ba8 100644 --- a/app/javascript/mastodon/locales/la.json +++ b/app/javascript/mastodon/locales/la.json @@ -1,6 +1,6 @@ { "about.contact": "Ratio:", - "about.domain_blocks.no_reason_available": "ratio abdere est", + "about.domain_blocks.no_reason_available": "Ratio abdere est", "account.account_note_header": "Annotatio", "account.badges.bot": "Robotum", "account.badges.group": "Congregatio", @@ -49,7 +49,7 @@ "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "embed.instructions": "Embed this status on your website by copying the code below.", - "emoji_button.food": "cibus et potus", + "emoji_button.food": "Cibus et potus", "emoji_button.people": "Homines", "emoji_button.search": "Quaerere...", "empty_column.account_timeline": "Hic nulla contributa!", @@ -57,13 +57,13 @@ "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", "explore.trending_statuses": "Contributa", - "generic.saved": "servavit", + "generic.saved": "Servavit", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", - "keyboard_shortcuts.back": "to navigate back", - "keyboard_shortcuts.blocked": "to open blocked users list", - "keyboard_shortcuts.boost": "to boost", - "keyboard_shortcuts.column": "to focus a status in one of the columns", - "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.back": "Re navigare", + "keyboard_shortcuts.blocked": "Aperire listam usorum obstructorum", + "keyboard_shortcuts.boost": "Inlustrare publicatio", + "keyboard_shortcuts.column": "Columnam dirigere", + "keyboard_shortcuts.compose": "TextArea Compositi Attendere", "keyboard_shortcuts.description": "Descriptio", "keyboard_shortcuts.direct": "to open direct messages column", "keyboard_shortcuts.down": "to move down in the list", diff --git a/app/javascript/mastodon/locales/ne.json b/app/javascript/mastodon/locales/ne.json index 0967ef424..95f8e703c 100644 --- a/app/javascript/mastodon/locales/ne.json +++ b/app/javascript/mastodon/locales/ne.json @@ -1 +1,53 @@ -{} +{ + "about.contact": "सम्पर्क:", + "about.disclaimer": "Mastodon नि:शुल्क, खुला स्रोत सफ्टवेयर, र Mastodon gGmbH को ट्रेडमार्क हो।", + "about.domain_blocks.no_reason_available": "कारण उपलब्ध छैन", + "about.domain_blocks.preamble": "Mastodon ले तपाइँलाई सामान्यतया फेडिभर्समा कुनै पनि अन्य सर्भरका सामग्री हेर्न र प्रयोगकर्ताहरूसँग अन्तरक्रिया गर्न दिन्छ। यी अपवादहरू हुन् जुन यस विशेष सर्भरमा बनाइएका छन्।", + "about.domain_blocks.silenced.title": "सीमित", + "about.domain_blocks.suspended.explanation": "यस सर्भरबाट कुनै पनि डेटा प्रशोधन, भण्डारण वा आदानप्रदान गरिने छैन, जसले यस सर्भरका प्रयोगकर्ताहरूसँग कुनै पनि अन्तरक्रिया वा सञ्चारलाई असम्भव बनाउँछ।", + "about.domain_blocks.suspended.title": "निलम्बित", + "about.not_available": "यो जानकारी यस सर्भरमा उपलब्ध गराइएको छैन।", + "about.powered_by": "{mastodon} द्वारा संचालित विकेन्द्रीकृत सामाजिक मिडिया", + "about.rules": "सर्भर नियमहरू", + "account.add_or_remove_from_list": "सूचीबाट थप्नुहोस् वा हटाउनुहोस्", + "account.badges.group": "समूह", + "account.block": "@{name} लाई ब्लक गर्नुहोस्", + "account.block_domain": "{domain} डोमेनलाई ब्लक गर्नुहोस्", + "account.block_short": "ब्लक", + "account.blocked": "ब्लक गरिएको", + "account.browse_more_on_origin_server": "मूल प्रोफाइलमा थप ब्राउज गर्नुहोस्", + "account.cancel_follow_request": "फलो अनुरोध रद्द गर्नुहोस", + "account.copy": "प्रोफाइलको लिङ्क प्रतिलिपि गर्नुहोस्", + "account.direct": "@{name} लाई निजी रूपमा उल्लेख गर्नुहोस्", + "account.disable_notifications": "@{name} ले पोस्ट गर्दा मलाई सूचित नगर्नुहोस्", + "account.domain_blocked": "डोमेन ब्लक गरिएको छ", + "account.edit_profile": "प्रोफाइल सम्पादन गर्नुहोस्", + "account.enable_notifications": "@{name} ले पोस्ट गर्दा मलाई सूचित गर्नुहोस्", + "account.endorse": "प्रोफाइलमा फिचर गर्नुहोस्", + "account.featured_tags.last_status_never": "कुनै पोस्ट छैन", + "account.follow": "फलो गर्नुहोस", + "account.followers.empty": "यस प्रयोगकर्तालाई अहिलेसम्म कसैले फलो गर्दैन।", + "account.follows.empty": "यो प्रयोगकर्ताले अहिलेसम्म कसैलाई फलो गरेको छैन।", + "account.go_to_profile": "प्रोफाइलमा जानुहोस्", + "account.hide_reblogs": "@{name} को बूस्टहरू लुकाउनुहोस्", + "account.link_verified_on": "यस लिङ्कको स्वामित्व {date} मा जाँच गरिएको थियो", + "account.media": "मिडिया", + "account.mention": "@{name} लाई उल्लेख गर्नुहोस्", + "account.no_bio": "कुनै विवरण प्रदान गरिएको छैन।", + "account.posts": "पोस्टहरू", + "account.requested": "स्वीकृतिको पर्खाइमा। फलो अनुरोध रद्द गर्न क्लिक गर्नुहोस्", + "account.requested_follow": "{name} ले तपाईंलाई फलो गर्न अनुरोध गर्नुभएको छ", + "account.share": "@{name} को प्रोफाइल सेयर गर्नुहोस्", + "account.show_reblogs": "@{name} को बूस्टहरू देखाउनुहोस्", + "account.statuses_counter": "{count, plural, one {{counter} पोस्ट} other {{counter} पोस्टहरू}}", + "account.unblock": "@{name} लाई अनब्लक गर्नुहोस्", + "account.unblock_domain": "{domain} डोमेनलाई अनब्लक गर्नुहोस्", + "account.unblock_short": "अनब्लक गर्नुहोस्", + "account.unendorse": "प्रोफाइलमा फिचर नगर्नुहोस्", + "account.unfollow": "अनफलो गर्नुहोस्", + "account_note.placeholder": "नोट लेख्न क्लिक गर्नुहोस्", + "admin.dashboard.retention.average": "औसत", + "admin.dashboard.retention.cohort_size": "नयाँ प्रयोगकर्ताहरू", + "alert.rate_limited.message": "कृपया {retry_time, time, medium} पछि पुन: प्रयास गर्नुहोस्।", + "alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।" +} diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index c4ce6f8cf..46fcc0116 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -224,6 +224,7 @@ "emoji_button.search_results": "Výsledky hľadania", "emoji_button.symbols": "Symboly", "emoji_button.travel": "Cestovanie a miesta", + "empty_column.account_hides_collections": "Tento užívateľ si zvolil nesprístupniť túto informáciu", "empty_column.account_suspended": "Účet bol pozastavený", "empty_column.account_timeline": "Nie sú tu žiadne príspevky!", "empty_column.account_unavailable": "Profil nedostupný", diff --git a/config/locales/activerecord.hr.yml b/config/locales/activerecord.hr.yml index b095244dd..a3e7d6d49 100644 --- a/config/locales/activerecord.hr.yml +++ b/config/locales/activerecord.hr.yml @@ -6,7 +6,9 @@ hr: expires_at: Krajnji rok options: Opcije user: + agreement: Ugovor o uslugama email: E-mail adresa + locale: Lokalitet password: Lozinka user/account: username: Korisničko ime @@ -18,3 +20,16 @@ hr: attributes: username: invalid: mora sadržavati samo slova, brojeve i _ + reserved: je rezervisano + admin/webhook: + attributes: + url: + invalid: nije validan URL + doorkeeper/application: + attributes: + website: + invalid: nije validan URL + import: + attributes: + data: + malformed: je neispravan diff --git a/config/locales/activerecord.ru.yml b/config/locales/activerecord.ru.yml index 14f9f61f6..92d85af4d 100644 --- a/config/locales/activerecord.ru.yml +++ b/config/locales/activerecord.ru.yml @@ -36,7 +36,7 @@ ru: status: attributes: reblog: - taken: поста уже существует + taken: пост уже существует user: attributes: email: diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 987788a7a..a6e74c483 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -534,6 +534,7 @@ en-GB: total_reported: Reports about them total_storage: Media attachments totals_time_period_hint_html: The totals displayed below include data for all time. + unknown_instance: There is currently no record of this domain on this server. invites: deactivate_all: Deactivate all filter: @@ -610,6 +611,7 @@ en-GB: created_at: Reported delete_and_resolve: Delete posts forwarded: Forwarded + forwarded_replies_explanation: This report is from a remote user and about remote content. It has been forwarded to you because the reported content is in reply to one of your users. forwarded_to: Forwarded to %{domain} mark_as_resolved: Mark as resolved mark_as_sensitive: Mark as sensitive @@ -1038,6 +1040,14 @@ en-GB: hint_html: Just one more thing! We need to confirm you're a human (this is so we can keep the spam out!). Solve the CAPTCHA below and click "Continue". title: Security check confirmations: + awaiting_review: Your e-mail address is confirmed! The %{domain} staff is now reviewing your registration. You will receive an e-mail if they approve your account! + awaiting_review_title: Your registration is being reviewed + clicking_this_link: clicking this link + login_link: log in + proceed_to_login_html: You can now proceed to %{login_link}. + redirect_to_app_html: You should have been redirected to the <strong>%{app_name}</strong> app. If that did not happen, try %{clicking_this_link} or manually return to the app. + registration_complete: Your registration on %{domain} is now complete! + welcome_title: Welcome, %{name}! wrong_email_hint: If that e-mail address is not correct, you can change it in account settings. delete_account: Delete account delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation. @@ -1099,6 +1109,7 @@ en-GB: functional: Your account is fully operational. pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved. redirecting_to: Your account is inactive because it is currently redirecting to %{acct}. + self_destruct: As %{domain} is closing down, you will only get limited access to your account. view_strikes: View past strikes against your account too_fast: Form submitted too fast, try again. use_security_key: Use security key @@ -1356,6 +1367,7 @@ en-GB: '86400': 1 day expires_in_prompt: Never generate: Generate invite link + invalid: This invite is not valid invited_by: 'You were invited by:' max_uses: one: 1 use @@ -1568,6 +1580,9 @@ en-GB: over_daily_limit: You have exceeded the limit of %{limit} scheduled posts for today over_total_limit: You have exceeded the limit of %{limit} scheduled posts too_soon: The scheduled date must be in the future + self_destruct: + lead_html: Unfortunately, <strong>%{domain}</strong> is permanently closing down. If you had an account there, you will not be able to continue using it, but you can still request a backup of your data. + title: This server is closing down sessions: activity: Last activity browser: Browser @@ -1736,6 +1751,7 @@ en-GB: default: "%b %d, %Y, %H:%M" month: "%b %Y" time: "%H:%M" + with_time_zone: "%b %d, %Y, %H:%M %Z" translation: errors: quota_exceeded: The server-wide usage quota for the translation service has been exceeded. diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 63779e5bd..caf253c69 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -656,6 +656,10 @@ sk: rules_check: action: Spravuj serverové pravidlá message_html: Neurčil/a si žiadne serverové pravidlá. + software_version_critical_check: + action: Pozri dostupné aktualizácie + software_version_patch_check: + action: Pozri dostupné aktualizácie upload_check_privacy_error: action: Pozri tu pre viac informácií upload_check_privacy_error_object_storage: From 4ad2e87c48f8ebdeeb6eab56dc195b9e28f7a5d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:45:30 +0100 Subject: [PATCH 69/73] Update dependency debug to v1.9.0 (#28315) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 738bc7b8b..4b39692d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -220,9 +220,9 @@ GEM database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) date (3.3.4) - debug (1.8.0) - irb (>= 1.5.0) - reline (>= 0.3.1) + debug (1.9.0) + irb (~> 1.10) + reline (>= 0.3.8) debug_inspector (1.1.0) devise (4.9.3) bcrypt (~> 3.0) From b5a1013ae36a0ab495aa727a397b9fc9f4340a2c Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 05:23:45 -0500 Subject: [PATCH 70/73] Combine `CLI::...` spec example subjects (#28285) --- spec/lib/mastodon/cli/accounts_spec.rb | 263 ++++++++---------------- spec/lib/mastodon/cli/ip_blocks_spec.rb | 89 ++++---- spec/lib/mastodon/cli/settings_spec.rb | 37 ++-- 3 files changed, 138 insertions(+), 251 deletions(-) diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index 06860c2ff..563f6e877 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -34,23 +34,21 @@ describe Mastodon::CLI::Accounts do let(:action) { :create } shared_examples 'a new user with given email address and username' do - it 'creates a new user with the specified email address' do - subject - - expect(User.find_by(email: options[:email])).to be_present - end - - it 'creates a new local account with the specified username' do - subject - - expect(Account.find_local('tootctl_username')).to be_present - end - - it 'returns "OK" and newly generated password' do + it 'creates user and accounts from options and displays success message' do allow(SecureRandom).to receive(:hex).and_return('test_password') expect { subject } - .to output_results("OK\nNew password: test_password") + .to output_results('OK', 'New password: test_password') + expect(user_from_options).to be_present + expect(account_from_options).to be_present + end + + def user_from_options + User.find_by(email: options[:email]) + end + + def account_from_options + Account.find_local('tootctl_username') end end @@ -199,14 +197,9 @@ describe Mastodon::CLI::Accounts do let(:arguments) { [user.account.username] } context 'when no option is provided' do - it 'returns a successful message' do + it 'returns a successful message and preserves user' do expect { subject } .to output_results('OK') - end - - it 'does not modify the user' do - subject - expect(user).to eq(user.reload) end end @@ -400,15 +393,10 @@ describe Mastodon::CLI::Accounts do context 'with --dry-run option' do let(:options) { { dry_run: true } } - it 'does not delete the specified user' do - subject - - expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false) - end - - it 'outputs a successful message in dry run mode' do + it 'outputs a successful message in dry run mode and does not delete the user' do expect { subject } .to output_results('OK (DRY RUN)') + expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false) end end @@ -435,15 +423,12 @@ describe Mastodon::CLI::Accounts do context 'with --dry-run option' do let(:options) { { email: account.user.email, dry_run: true } } - it 'does not delete the user' do - subject - - expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false) - end - - it 'outputs a successful message in dry run mode' do + it 'outputs a successful message in dry run mode and does not delete the user' do expect { subject } .to output_results('OK (DRY RUN)') + expect(delete_account_service) + .to_not have_received(:call) + .with(account, reserve_email: false) end end @@ -482,20 +467,19 @@ describe Mastodon::CLI::Accounts do context 'when the number is positive' do let(:options) { { number: 2 } } - it 'approves the earliest n pending registrations' do + it 'approves the earliest n pending registrations but not the remaining ones' do subject - n_earliest_pending_registrations = User.order(created_at: :asc).first(options[:number]) - expect(n_earliest_pending_registrations.all?(&:approved?)).to be(true) + expect(pending_registrations.all?(&:approved?)).to be(false) end - it 'does not approve the remaining pending registrations' do - subject + def n_earliest_pending_registrations + User.order(created_at: :asc).first(options[:number]) + end - pending_registrations = User.order(created_at: :asc).last(total_users - options[:number]) - - expect(pending_registrations.all?(&:approved?)).to be(false) + def pending_registrations + User.order(created_at: :asc).last(total_users - options[:number]) end end @@ -512,15 +496,10 @@ describe Mastodon::CLI::Accounts do context 'when the given number is greater than the number of users' do let(:options) { { number: total_users * 2 } } - it 'approves all users' do - subject - - expect(User.pluck(:approved).all?(true)).to be(true) - end - - it 'does not raise any error' do + it 'approves all users and does not raise any error' do expect { subject } .to_not raise_error + expect(User.pluck(:approved).all?(true)).to be(true) end end end @@ -575,18 +554,13 @@ describe Mastodon::CLI::Accounts do stub_parallelize_with_progress! end - it 'makes all local accounts follow the target account' do - subject - + it 'displays a successful message and makes all local accounts follow the target account' do + expect { subject } + .to output_results("OK, followed target from #{Account.local.count} accounts") expect(follow_service).to have_received(:call).with(follower_bob, target_account, any_args).once expect(follow_service).to have_received(:call).with(follower_rony, target_account, any_args).once expect(follow_service).to have_received(:call).with(follower_charles, target_account, any_args).once end - - it 'displays a successful message' do - expect { subject } - .to output_results("OK, followed target from #{Account.local.count} accounts") - end end end @@ -618,18 +592,13 @@ describe Mastodon::CLI::Accounts do stub_parallelize_with_progress! end - it 'makes all local accounts unfollow the target account' do - subject - + it 'displays a successful message and makes all local accounts unfollow the target account' do + expect { subject } + .to output_results('OK, unfollowed target from 3 accounts') expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once expect(unfollow_service).to have_received(:call).with(follower_ana, target_account).once end - - it 'displays a successful message' do - expect { subject } - .to output_results('OK, unfollowed target from 3 accounts') - end end end @@ -651,22 +620,17 @@ describe Mastodon::CLI::Accounts do let(:user) { account.user } let(:arguments) { [account.username] } - it 'creates a new backup for the specified user' do - expect { subject }.to change { user.backups.count }.by(1) - end - - it 'creates a backup job' do - allow(BackupWorker).to receive(:perform_async) - - subject - latest_backup = user.backups.last + before { allow(BackupWorker).to receive(:perform_async) } + it 'creates a new backup and backup job for the specified user and outputs success message' do + expect { subject } + .to change { user.backups.count }.by(1) + .and output_results('OK') expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once end - it 'displays a successful message' do - expect { subject } - .to output_results('OK') + def latest_backup + user.backups.last end end end @@ -963,15 +927,15 @@ describe Mastodon::CLI::Accounts do expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once end + end - context 'when the given username is not found' do - let(:arguments) { ['non_existent_username'] } + context 'when the given username is not found' do + let(:arguments) { ['non_existent_username'] } - it 'exits with an error message when the specified username is not found' do - expect { subject } - .to output_results('No such account') - .and raise_error(SystemExit) - end + it 'exits with an error message when the specified username is not found' do + expect { subject } + .to output_results('No such account') + .and raise_error(SystemExit) end end @@ -1071,15 +1035,10 @@ describe Mastodon::CLI::Accounts do allow(from_account).to receive(:destroy) end - it 'merges "from_account" into "to_account"' do + it 'merges `from_account` into `to_account` and deletes `from_account`' do subject expect(to_account).to have_received(:merge_with!).with(from_account).once - end - - it 'deletes "from_account"' do - subject - expect(from_account).to have_received(:destroy).once end end @@ -1099,15 +1058,10 @@ describe Mastodon::CLI::Accounts do allow(from_account).to receive(:destroy) end - it 'merges "from_account" into "to_account"' do + it 'merges "from_account" into "to_account" and deletes from_account' do subject expect(to_account).to have_received(:merge_with!).with(from_account).once - end - - it 'deletes "from_account"' do - subject - expect(from_account).to have_received(:destroy) end end @@ -1134,28 +1088,23 @@ describe Mastodon::CLI::Accounts do stub_request(:head, 'https://example.net/users/tales').to_return(status: 200) end - it 'deletes all inactive remote accounts that longer exist in the origin server' do - subject - + def expect_delete_inactive_remote_accounts expect(delete_account_service).to have_received(:call).with(bob, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once end - it 'does not delete any active remote account that still exists in the origin server' do - subject - + def expect_not_delete_active_accounts expect(delete_account_service).to_not have_received(:call).with(tom, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(ana, reserve_username: false) expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) end - it 'touches inactive remote accounts that have not been deleted' do - expect { subject }.to(change { tales.reload.updated_at }) - end - - it 'displays the summary correctly' do + it 'touches inactive remote accounts that have not been deleted and summarizes activity' do expect { subject } - .to output_results('Visited 5 accounts, removed 2') + .to change { tales.reload.updated_at } + .and output_results('Visited 5 accounts, removed 2') + expect_delete_inactive_remote_accounts + expect_not_delete_active_accounts end end @@ -1168,16 +1117,15 @@ describe Mastodon::CLI::Accounts do stub_request(:head, 'https://example.net/users/tales').to_return(status: 404) end - it 'deletes inactive remote accounts that longer exist in the specified domain' do - subject - + def expect_delete_inactive_remote_accounts expect(delete_account_service).to have_received(:call).with(gon, reserve_username: false).once expect(delete_account_service).to have_received(:call).with(tales, reserve_username: false).once end - it 'displays the summary correctly' do + it 'displays the summary correctly and deletes inactive remote accounts' do expect { subject } .to output_results('Visited 2 accounts, removed 2') + expect_delete_inactive_remote_accounts end end @@ -1189,15 +1137,14 @@ describe Mastodon::CLI::Accounts do stub_request(:head, 'https://example.net/users/gon').to_return(status: 200) end - it 'skips accounts from the unavailable domain' do - subject - + def expect_skip_accounts_from_unavailable_domain expect(delete_account_service).to_not have_received(:call).with(tales, reserve_username: false) end - it 'displays the summary correctly' do + it 'displays the summary correctly and skip accounts from unavailable domains' do expect { subject } .to output_results("Visited 5 accounts, removed 0\nThe following domains were not available during the check:\n example.net") + expect_skip_accounts_from_unavailable_domain end end @@ -1268,25 +1215,14 @@ describe Mastodon::CLI::Accounts do before do accounts.each { |account| target_account.follow!(account) } - end - - it 'resets all "following" relationships from the target account' do - subject - - expect(target_account.reload.following).to be_empty - end - - it 'calls BootstrapTimelineWorker once to rebuild the timeline' do allow(BootstrapTimelineWorker).to receive(:perform_async) - - subject - - expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once end - it 'displays a successful message' do + it 'resets following relationships and displays a successful message and rebuilds timeline' do expect { subject } .to output_results("Processed #{total_relationships} relationships") + expect(target_account.reload.following).to be_empty + expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once end end @@ -1297,15 +1233,10 @@ describe Mastodon::CLI::Accounts do accounts.each { |account| account.follow!(target_account) } end - it 'resets all "followers" relationships from the target account' do - subject - - expect(target_account.reload.followers).to be_empty - end - - it 'displays a successful message' do + it 'resets followers relationships and displays a successful message' do expect { subject } .to output_results("Processed #{total_relationships} relationships") + expect(target_account.reload.followers).to be_empty end end @@ -1315,31 +1246,15 @@ describe Mastodon::CLI::Accounts do before do accounts.first(2).each { |account| account.follow!(target_account) } accounts.last(1).each { |account| target_account.follow!(account) } - end - - it 'resets all "followers" relationships from the target account' do - subject - - expect(target_account.reload.followers).to be_empty - end - - it 'resets all "following" relationships from the target account' do - subject - - expect(target_account.reload.following).to be_empty - end - - it 'calls BootstrapTimelineWorker once to rebuild the timeline' do allow(BootstrapTimelineWorker).to receive(:perform_async) - - subject - - expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once end - it 'displays a successful message' do + it 'resets followers and following and displays a successful message and rebuilds timeline' do expect { subject } .to output_results("Processed #{total_relationships} relationships") + expect(target_account.reload.followers).to be_empty + expect(target_account.reload.following).to be_empty + expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once end end end @@ -1360,57 +1275,51 @@ describe Mastodon::CLI::Accounts do stub_parallelize_with_progress! end - it 'prunes all remote accounts with no interactions with local users' do - subject - + def expect_prune_remote_accounts_without_interaction prunable_account_ids = prunable_accounts.pluck(:id) expect(Account.where(id: prunable_account_ids).count).to eq(0) end - it 'displays a successful message' do + it 'displays a successful message and handles accounts correctly' do expect { subject } .to output_results("OK, pruned #{prunable_accounts.size} accounts") + expect_prune_remote_accounts_without_interaction + expect_not_prune_local_accounts + expect_not_prune_bot_accounts + expect_not_prune_group_accounts + expect_not_prune_mentioned_accounts end - it 'does not prune local accounts' do - subject - + def expect_not_prune_local_accounts expect(Account.exists?(id: local_account.id)).to be(true) end - it 'does not prune bot accounts' do - subject - + def expect_not_prune_bot_accounts expect(Account.exists?(id: bot_account.id)).to be(true) end - it 'does not prune group accounts' do - subject - + def expect_not_prune_group_accounts expect(Account.exists?(id: group_account.id)).to be(true) end - it 'does not prune accounts that have been mentioned' do - subject - + def expect_not_prune_mentioned_accounts expect(Account.exists?(id: mentioned_account.id)).to be true end context 'with --dry-run option' do let(:options) { { dry_run: true } } - it 'does not prune any account' do - subject - + def expect_no_account_prunes prunable_account_ids = prunable_accounts.pluck(:id) expect(Account.where(id: prunable_account_ids).count).to eq(prunable_accounts.size) end - it 'displays a successful message with (DRY RUN)' do + it 'displays a successful message with (DRY RUN) and doesnt prune anything' do expect { subject } .to output_results("OK, pruned #{prunable_accounts.size} accounts (DRY RUN)") + expect_no_account_prunes end end end diff --git a/spec/lib/mastodon/cli/ip_blocks_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb index dc967a69c..1d6c47268 100644 --- a/spec/lib/mastodon/cli/ip_blocks_spec.rb +++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb @@ -33,26 +33,25 @@ describe Mastodon::CLI::IpBlocks do let(:arguments) { ip_list } shared_examples 'ip address blocking' do - it 'blocks all specified IP addresses' do - subject - - blocked_ip_addresses = IpBlock.where(ip: ip_list).pluck(:ip) - expected_ip_addresses = ip_list.map { |ip| IPAddr.new(ip) } - - expect(blocked_ip_addresses).to match_array(expected_ip_addresses) + def blocked_ip_addresses + IpBlock.where(ip: ip_list).pluck(:ip) end - it 'sets the severity for all blocked IP addresses' do - subject - - blocked_ips_severity = IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity]) - - expect(blocked_ips_severity).to be(true) + def expected_ip_addresses + ip_list.map { |ip| IPAddr.new(ip) } end - it 'displays a success message with a summary' do + def blocked_ips_severity + IpBlock.where(ip: ip_list).pluck(:severity).all?(options[:severity]) + end + + it 'blocks and sets severity for ip address and displays summary' do expect { subject } .to output_results("Added #{ip_list.size}, skipped 0, failed 0") + expect(blocked_ip_addresses) + .to match_array(expected_ip_addresses) + expect(blocked_ips_severity) + .to be(true) end end @@ -64,17 +63,13 @@ describe Mastodon::CLI::IpBlocks do let!(:blocked_ip) { IpBlock.create(ip: ip_list.last, severity: options[:severity]) } let(:arguments) { ip_list } - it 'skips the already blocked IP address' do - allow(IpBlock).to receive(:new).and_call_original + before { allow(IpBlock).to receive(:new).and_call_original } - subject - - expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last) - end - - it 'displays the correct summary' do + it 'skips already block ip and displays the correct summary' do expect { subject } .to output_results("#{ip_list.last} is already blocked\nAdded #{ip_list.size - 1}, skipped 1, failed 0") + + expect(IpBlock).to_not have_received(:new).with(ip: ip_list.last) end context 'with --force option' do @@ -179,15 +174,10 @@ describe Mastodon::CLI::IpBlocks do ip_list.each { |ip| IpBlock.create(ip: ip, severity: :no_access) } end - it 'removes exact IP blocks' do - subject - - expect(IpBlock.where(ip: ip_list)).to_not exist - end - - it 'displays success message with a summary' do + it 'removes exact ip blocks and displays success message with a summary' do expect { subject } .to output_results("Removed #{ip_list.size}, skipped 0") + expect(IpBlock.where(ip: ip_list)).to_not exist end end @@ -198,16 +188,19 @@ describe Mastodon::CLI::IpBlocks do let(:arguments) { ['192.168.0.5', '10.0.1.50'] } let(:options) { { force: true } } - it 'removes blocks for IP ranges that cover given IP(s)' do + it 'removes blocks for IP ranges that cover given IP(s) and keeps other ranges' do subject - expect(IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id])).to_not exist + expect(covered_ranges).to_not exist + expect(other_ranges).to exist end - it 'does not remove other IP ranges' do - subject + def covered_ranges + IpBlock.where(id: [first_ip_range_block.id, second_ip_range_block.id]) + end - expect(IpBlock.where(id: third_ip_range_block.id)).to exist + def other_ranges + IpBlock.where(id: third_ip_range_block.id) end end @@ -215,14 +208,12 @@ describe Mastodon::CLI::IpBlocks do let(:unblocked_ip) { '192.0.2.1' } let(:arguments) { [unblocked_ip] } - it 'skips the IP address' do + it 'skips the IP address and displays summary' do expect { subject } - .to output_results("#{unblocked_ip} is not yet blocked") - end - - it 'displays the summary correctly' do - expect { subject } - .to output_results('Removed 0, skipped 1') + .to output_results( + "#{unblocked_ip} is not yet blocked", + 'Removed 0, skipped 1' + ) end end @@ -230,14 +221,12 @@ describe Mastodon::CLI::IpBlocks do let(:invalid_ip) { '320.15.175.0' } let(:arguments) { [invalid_ip] } - it 'skips the invalid IP address' do + it 'skips the invalid IP address and displays summary' do expect { subject } - .to output_results("#{invalid_ip} is invalid") - end - - it 'displays the summary correctly' do - expect { subject } - .to output_results('Removed 0, skipped 1') + .to output_results( + "#{invalid_ip} is invalid", + 'Removed 0, skipped 1' + ) end end @@ -265,7 +254,7 @@ describe Mastodon::CLI::IpBlocks do .to output_results("#{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}\n#{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix}") end - it 'does not export bloked IPs with different severities' do + it 'does not export blocked IPs with different severities' do expect { subject } .to_not output_results("#{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix}") end @@ -279,7 +268,7 @@ describe Mastodon::CLI::IpBlocks do .to output_results("deny #{first_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};\ndeny #{second_ip_range_block.ip}/#{second_ip_range_block.ip.prefix};") end - it 'does not export bloked IPs with different severities' do + it 'does not export blocked IPs with different severities' do expect { subject } .to_not output_results("deny #{third_ip_range_block.ip}/#{first_ip_range_block.ip.prefix};") end diff --git a/spec/lib/mastodon/cli/settings_spec.rb b/spec/lib/mastodon/cli/settings_spec.rb index 02d1042c5..568ee0039 100644 --- a/spec/lib/mastodon/cli/settings_spec.rb +++ b/spec/lib/mastodon/cli/settings_spec.rb @@ -20,37 +20,29 @@ describe Mastodon::CLI::Settings do describe '#open' do let(:action) { :open } - it 'changes "registrations_mode" to "open"' do - expect { subject }.to change(Setting, :registrations_mode).from(nil).to('open') - end - - it 'displays success message' do + it 'changes "registrations_mode" to "open" and displays success' do expect { subject } - .to output_results('OK') + .to change(Setting, :registrations_mode).from(nil).to('open') + .and output_results('OK') end end describe '#approved' do let(:action) { :approved } - it 'changes "registrations_mode" to "approved"' do - expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved') - end - - it 'displays success message' do + it 'changes "registrations_mode" to "approved" and displays success' do expect { subject } - .to output_results('OK') + .to change(Setting, :registrations_mode).from(nil).to('approved') + .and output_results('OK') end context 'with --require-reason' do let(:options) { { require_reason: true } } - it 'changes "registrations_mode" to "approved"' do - expect { subject }.to change(Setting, :registrations_mode).from(nil).to('approved') - end - - it 'sets "require_invite_text" to "true"' do - expect { subject }.to change(Setting, :require_invite_text).from(false).to(true) + it 'changes registrations_mode and require_invite_text' do + expect { subject } + .to change(Setting, :registrations_mode).from(nil).to('approved') + .and change(Setting, :require_invite_text).from(false).to(true) end end end @@ -58,13 +50,10 @@ describe Mastodon::CLI::Settings do describe '#close' do let(:action) { :close } - it 'changes "registrations_mode" to "none"' do - expect { subject }.to change(Setting, :registrations_mode).from(nil).to('none') - end - - it 'displays success message' do + it 'changes "registrations_mode" to "none" and displays success' do expect { subject } - .to output_results('OK') + .to change(Setting, :registrations_mode).from(nil).to('none') + .and output_results('OK') end end end From f3864db4090b8cec212fb99c3dabccfb6a6c7642 Mon Sep 17 00:00:00 2001 From: Claire <claire.github-309c@sitedethib.com> Date: Mon, 11 Dec 2023 15:23:30 +0100 Subject: [PATCH 71/73] Fix notification sounds (#28316) --- app/javascript/mastodon/actions/notifications_typed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/notifications_typed.ts b/app/javascript/mastodon/actions/notifications_typed.ts index 7e51fa51e..176362f4b 100644 --- a/app/javascript/mastodon/actions/notifications_typed.ts +++ b/app/javascript/mastodon/actions/notifications_typed.ts @@ -18,6 +18,6 @@ export const notificationsUpdate = createAction( playSound: boolean; }) => ({ payload: args, - meta: { playSound: playSound ? { sound: 'boop' } : undefined }, + meta: { sound: playSound ? 'boop' : undefined }, }), ); From 9dfa94063626bc2351e2098e226deee7b0e3eff8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:58:10 +0100 Subject: [PATCH 72/73] Update babel monorepo to v7.23.6 (#28319) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 159 +++++++++++++++++++++++++++--------------------------- 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0ef881126..71f1ad160 100644 --- a/yarn.lock +++ b/yarn.lock @@ -52,7 +52,7 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": version: 7.23.5 resolution: "@babel/compat-data@npm:7.23.5" checksum: 081278ed46131a890ad566a59c61600a5f9557bd8ee5e535890c8548192532ea92590742fd74bd9db83d74c669ef8a04a7e1c85cdea27f960233e3b83c3a957c @@ -60,37 +60,37 @@ __metadata: linkType: hard "@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.1, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1": - version: 7.23.5 - resolution: "@babel/core@npm:7.23.5" + version: 7.23.6 + resolution: "@babel/core@npm:7.23.6" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.23.5" - "@babel/generator": "npm:^7.23.5" - "@babel/helper-compilation-targets": "npm:^7.22.15" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.23.5" - "@babel/parser": "npm:^7.23.5" + "@babel/helpers": "npm:^7.23.6" + "@babel/parser": "npm:^7.23.6" "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.5" - "@babel/types": "npm:^7.23.5" + "@babel/traverse": "npm:^7.23.6" + "@babel/types": "npm:^7.23.6" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 311a512a870ee330a3f9a7ea89e5df790b2b5af0b1bd98b10b4edc0de2ac440f0df4d69ea2c0ee38a4b89041b9a495802741d93603be7d4fd834ec8bb6970bd2 + checksum: a02bae7d916029b70706dc301535e1b31e5d216f55d4ee6f64a15825c6b69ee2c14c52a213d1497ec414e925ed4e9d897d41fb0d75df9fea28ed2c0008790e31 languageName: node linkType: hard -"@babel/generator@npm:^7.23.5, @babel/generator@npm:^7.7.2": - version: 7.23.5 - resolution: "@babel/generator@npm:7.23.5" +"@babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": + version: 7.23.6 + resolution: "@babel/generator@npm:7.23.6" dependencies: - "@babel/types": "npm:^7.23.5" + "@babel/types": "npm:^7.23.6" "@jridgewell/gen-mapping": "npm:^0.3.2" "@jridgewell/trace-mapping": "npm:^0.3.17" jsesc: "npm:^2.5.1" - checksum: 14c6e874f796c4368e919bed6003bb0adc3ce837760b08f9e646d20aeb5ae7d309723ce6e4f06bcb4a2b5753145446c8e4425851380f695e40e71e1760f49e7b + checksum: 53540e905cd10db05d9aee0a5304e36927f455ce66f95d1253bb8a179f286b88fa7062ea0db354c566fe27f8bb96567566084ffd259f8feaae1de5eccc8afbda languageName: node linkType: hard @@ -122,16 +122,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6": - version: 7.22.15 - resolution: "@babel/helper-compilation-targets@npm:7.22.15" +"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" dependencies: - "@babel/compat-data": "npm:^7.22.9" - "@babel/helper-validator-option": "npm:^7.22.15" - browserslist: "npm:^4.21.9" + "@babel/compat-data": "npm:^7.23.5" + "@babel/helper-validator-option": "npm:^7.23.5" + browserslist: "npm:^4.22.2" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: 45b9286861296e890f674a3abb199efea14a962a27d9b8adeb44970a9fd5c54e73a9e342e8414d2851cf4f98d5994537352fbce7b05ade32e9849bbd327f9ff1 + checksum: ba38506d11185f48b79abf439462ece271d3eead1673dd8814519c8c903c708523428806f05f2ec5efd0c56e4e278698fac967e5a4b5ee842c32415da54bc6fa languageName: node linkType: hard @@ -342,14 +342,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/helpers@npm:7.23.5" +"@babel/helpers@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helpers@npm:7.23.6" dependencies: "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.5" - "@babel/types": "npm:^7.23.5" - checksum: a37e2728eb4378a4888e5d614e28de7dd79b55ac8acbecd0e5c761273e2a02a8f33b34b1932d9069db55417ace2937cbf8ec37c42f1030ce6d228857d7ccaa4f + "@babel/traverse": "npm:^7.23.6" + "@babel/types": "npm:^7.23.6" + checksum: df1cf6607676ad36f52f652ec03536f2732d70aef5e76dba5c964e34d49f3c2d3dcf9fb3740db359f53071d74b64606a833d5ba156f79f437f71bfe06e2e7e19 languageName: node linkType: hard @@ -364,12 +364,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/parser@npm:7.23.5" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/parser@npm:7.23.6" bin: parser: ./bin/babel-parser.js - checksum: 3356aa90d7bafb4e2c7310e7c2c3d443c4be4db74913f088d3d577a1eb914ea4188e05fd50a47ce907a27b755c4400c4e3cbeee73dbeb37761f6ca85954f5a20 + checksum: 6f76cd5ccae1fa9bcab3525b0865c6222e9c1d22f87abc69f28c5c7b2c8816a13361f5bd06bddbd5faf903f7320a8feba02545c981468acec45d12a03db7755e languageName: node linkType: hard @@ -836,14 +836,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-for-of@npm:7.23.3" +"@babel/plugin-transform-for-of@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/plugin-transform-for-of@npm:7.23.6" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8a36202cfee312ba80e509c7c2131e6773524e572b4dc64a8ee95bd912634fdeb5ea91c6c7747ee30e03562d0f0d333f88ed7dbb929b36b60b8d74189189e12f + checksum: 46681b6ab10f3ca2d961f50d4096b62ab5d551e1adad84e64be1ee23e72eb2f26a1e30e617e853c74f1349fffe4af68d33921a128543b6f24b6d46c09a3e2aec languageName: node linkType: hard @@ -1200,8 +1201,8 @@ __metadata: linkType: hard "@babel/plugin-transform-runtime@npm:^7.22.4": - version: 7.23.4 - resolution: "@babel/plugin-transform-runtime@npm:7.23.4" + version: 7.23.6 + resolution: "@babel/plugin-transform-runtime@npm:7.23.6" dependencies: "@babel/helper-module-imports": "npm:^7.22.15" "@babel/helper-plugin-utils": "npm:^7.22.5" @@ -1211,7 +1212,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6ac29012550cdd10b65ec43fef0c7f43904ec458c43d597f627d8f52807413e57ea94e3986dbace576d734e67c2d09be5e43e77c72567d18f8c4ac5e19844625 + checksum: 94a7ee92f073df53fd8bebf9ed391a95553716077da1c6c3a57f10f042358c938495d55e6b09b4b50544c01f03560c4770c17698e1c24817a15d3668e8231249 languageName: node linkType: hard @@ -1333,11 +1334,11 @@ __metadata: linkType: hard "@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.22.4": - version: 7.23.5 - resolution: "@babel/preset-env@npm:7.23.5" + version: 7.23.6 + resolution: "@babel/preset-env@npm:7.23.6" dependencies: "@babel/compat-data": "npm:^7.23.5" - "@babel/helper-compilation-targets": "npm:^7.22.15" + "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/helper-validator-option": "npm:^7.23.5" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.23.3" @@ -1377,7 +1378,7 @@ __metadata: "@babel/plugin-transform-dynamic-import": "npm:^7.23.4" "@babel/plugin-transform-exponentiation-operator": "npm:^7.23.3" "@babel/plugin-transform-export-namespace-from": "npm:^7.23.4" - "@babel/plugin-transform-for-of": "npm:^7.23.3" + "@babel/plugin-transform-for-of": "npm:^7.23.6" "@babel/plugin-transform-function-name": "npm:^7.23.3" "@babel/plugin-transform-json-strings": "npm:^7.23.4" "@babel/plugin-transform-literals": "npm:^7.23.3" @@ -1418,7 +1419,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2a0e1274dec045186e131c6433659b75492583290e8d41633c616f6bff829cb2e4b2f9a57f556283a54db3bd6aa697911e56a36f607911a29b731c445a5b5a06 + checksum: 5b24d179af52f082d04b9b98cc4777e37bf31a97cef5a91d8917e996dbd75f2f743c88c40f80744cb8529355bb674619d150c0260c32d834aa4067e21d0c8962 languageName: node linkType: hard @@ -1483,11 +1484,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.23.5 - resolution: "@babel/runtime@npm:7.23.5" + version: 7.23.6 + resolution: "@babel/runtime@npm:7.23.6" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: ca679cc91bb7e424bc2db87bb58cc3b06ade916b9adb21fbbdc43e54cdaacb3eea201ceba2a0464b11d2eb65b9fe6a6ffcf4d7521fa52994f19be96f1af14788 + checksum: d886954e985ef8e421222f7a2848884d96a752e0020d3078b920dd104e672fdf23bcc6f51a44313a048796319f1ac9d09c2c88ec8cbb4e1f09174bcd3335b9ff languageName: node linkType: hard @@ -1502,32 +1503,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:7, @babel/traverse@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/traverse@npm:7.23.5" +"@babel/traverse@npm:7, @babel/traverse@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/traverse@npm:7.23.6" dependencies: "@babel/code-frame": "npm:^7.23.5" - "@babel/generator": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" "@babel/helper-environment-visitor": "npm:^7.22.20" "@babel/helper-function-name": "npm:^7.23.0" "@babel/helper-hoist-variables": "npm:^7.22.5" "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.23.5" - "@babel/types": "npm:^7.23.5" - debug: "npm:^4.1.0" + "@babel/parser": "npm:^7.23.6" + "@babel/types": "npm:^7.23.6" + debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: c5ea793080ca6719b0a1612198fd25e361cee1f3c14142d7a518d2a1eeb5c1d21f7eec1b26c20ea6e1ddd8ed12ab50b960ff95ffd25be353b6b46e1b54d6f825 + checksum: 5b4ebb94a00a7e1daf111e4b0b45a7998d5b7598637a14e75e855e88cc1b702789e09a958726b5d599a003be1e9032dbdfde4b88ea6061332228738950d5582d languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.23.5 - resolution: "@babel/types@npm:7.23.5" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.23.6 + resolution: "@babel/types@npm:7.23.6" dependencies: "@babel/helper-string-parser": "npm:^7.23.4" "@babel/helper-validator-identifier": "npm:^7.22.20" to-fast-properties: "npm:^2.0.0" - checksum: 7dd5e2f59828ed046ad0b06b039df2524a8b728d204affb4fc08da2502b9dd3140b1356b5166515d229dc811539a8b70dcd4bc507e06d62a89f4091a38d0b0fb + checksum: 42cefce8a68bd09bb5828b4764aa5586c53c60128ac2ac012e23858e1c179347a4aac9c66fc577994fbf57595227611c5ec8270bf0cfc94ff033bbfac0550b70 languageName: node linkType: hard @@ -5170,17 +5171,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.21.9, browserslist@npm:^4.22.1": - version: 4.22.1 - resolution: "browserslist@npm:4.22.1" +"browserslist@npm:^4.0.0, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.1, browserslist@npm:^4.22.2": + version: 4.22.2 + resolution: "browserslist@npm:4.22.2" dependencies: - caniuse-lite: "npm:^1.0.30001541" - electron-to-chromium: "npm:^1.4.535" - node-releases: "npm:^2.0.13" + caniuse-lite: "npm:^1.0.30001565" + electron-to-chromium: "npm:^1.4.601" + node-releases: "npm:^2.0.14" update-browserslist-db: "npm:^1.0.13" bin: browserslist: cli.js - checksum: 6810f2d63f171d0b7b8d38cf091708e00cb31525501810a507839607839320d66e657293b0aa3d7f051ecbc025cb07390a90c037682c1d05d12604991e41050b + checksum: 2a331aab90503130043ca41dd5d281fa1e89d5e076d07a2d75e76bf4d693bd56e73d5abcd8c4f39119da6328d450578c216cf1cd5c99b82d8a90a2ae6271b465 languageName: node linkType: hard @@ -5408,10 +5409,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001541": - version: 1.0.30001561 - resolution: "caniuse-lite@npm:1.0.30001561" - checksum: 6e84c84026fee53edbdbb5aded7a04a036aae4c2e367cf6bdc90c6783a591e2fdcfcdebcc4e774aca61092e542a61200c8c16b06659396492426033c4dbcc618 +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001565": + version: 1.0.30001568 + resolution: "caniuse-lite@npm:1.0.30001568" + checksum: 13f01e5a2481134bd61cf565ce9fecbd8e107902927a0dcf534230a92191a81f1715792170f5f39719c767c3a96aa6df9917a8d5601f15bbd5e4041a8cfecc99 languageName: node linkType: hard @@ -6413,7 +6414,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -6955,10 +6956,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.535": - version: 1.4.576 - resolution: "electron-to-chromium@npm:1.4.576" - checksum: b0b9e7ba803bf93ffac9cb830ed2b0e0eb07f20066127065f9ab9e08e4e6a5812040e03d76f6ee9bc59e03fb938fd414e83d4883b29111303e9e88633cf2dce4 +"electron-to-chromium@npm:^1.4.601": + version: 1.4.609 + resolution: "electron-to-chromium@npm:1.4.609" + checksum: 9675a79388acbaff5953a4c61589af7da93e0d1f9d6a3b284c7630f10126eb0998557b07448514214d5a3d19025310039b55f405ab701b1253130fc94907f743 languageName: node linkType: hard @@ -11930,10 +11931,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.13": - version: 2.0.13 - resolution: "node-releases@npm:2.0.13" - checksum: 2fb44bf70fc949d27f3a48a7fd1a9d1d603ddad4ccd091f26b3fb8b1da976605d919330d7388ccd55ca2ade0dc8b2e12841ba19ef249c8bb29bf82532d401af7 +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 199fc93773ae70ec9969bc6d5ac5b2bbd6eb986ed1907d751f411fef3ede0e4bfdb45ceb43711f8078bea237b6036db8b1bf208f6ff2b70c7d615afd157f3ab9 languageName: node linkType: hard From 2c6369918c61a7f948f39926f987fcfa0b02fb82 Mon Sep 17 00:00:00 2001 From: Matt Jankowski <matt@jankowski.online> Date: Mon, 11 Dec 2023 09:58:29 -0500 Subject: [PATCH 73/73] Fix `Style/RedundantArgument` cop (#28321) --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 48d9119fb..135bbb0fd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -114,7 +114,7 @@ module ApplicationHelper end def fa_icon(icon, attributes = {}) - class_names = attributes[:class]&.split(' ') || [] + class_names = attributes[:class]&.split || [] class_names << 'fa' class_names += icon.split.map { |cl| "fa-#{cl}" }