Tag posts and comments with language (fixes #440) (#2269)

* Tag posts and comments with language (fixes #440)

* Untangle PostView tests

* Implement test for PostView language query

* Store languages directly in database

* finish moving languages into db, it compiles

* update post_view

* serde skip Language.id field

* add local_user_language table, other changes suggested in review

* add code for local_user_discussion_language_view

* Remove unnecessary clones in db view converteres

* Fixing up some table and join issues.

* Clearing the current languages.

* Fix formatting.

* update user languages in single transaction

* proper test for user language queries

* Some fixes for all / missing user languages. (#2404)

* Some fixes for all / missing user languages.

* Adding back in transaction.

* fix test

Co-authored-by: Dessalines <tyhou13@gmx.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
Nutomic 2022-08-18 19:11:19 +00:00 committed by GitHub
parent c89006c94a
commit e4a49b6eab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1016 additions and 323 deletions

276
Cargo.lock generated
View file

@ -38,9 +38,9 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07520b54fc0f22ad30b90399b2a2689c6e5c113df0642ca3fa2f7ee823e54126"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -135,8 +135,8 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
dependencies = [
"quote 1.0.18",
"syn 1.0.96",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -286,9 +286,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7525bedf54704abb1d469e88d7e7e9226df73778798a69cea5022d53b2ae91bc"
dependencies = [
"actix-router",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -297,9 +297,9 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -345,9 +345,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.57"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
[[package]]
name = "arrayvec"
@ -390,9 +390,9 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -401,9 +401,9 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -860,10 +860,10 @@ checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
dependencies = [
"fnv",
"ident_case",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"strsim",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -874,10 +874,10 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"strsim",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -888,10 +888,10 @@ checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
dependencies = [
"fnv",
"ident_case",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"strsim",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -901,8 +901,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core 0.12.4",
"quote 1.0.18",
"syn 1.0.96",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -912,8 +912,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core 0.13.4",
"quote 1.0.18",
"syn 1.0.96",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -923,8 +923,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
dependencies = [
"darling_core 0.14.1",
"quote 1.0.18",
"syn 1.0.96",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -961,9 +961,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling 0.12.4",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -973,9 +973,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4"
dependencies = [
"darling 0.14.1",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -985,7 +985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
"derive_builder_core 0.10.2",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -995,7 +995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68"
dependencies = [
"derive_builder_core 0.11.2",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -1005,10 +1005,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"rustc_version",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -1053,9 +1053,9 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -1145,9 +1145,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6430bef5fcbfa22f3c431f05a14254d45f41ab634cabe09fad82e98d4f9fdc8b"
dependencies = [
"darling 0.13.4",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -1158,9 +1158,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "email-encoding"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b91dddc343e7eaa27f9764e5bffe57370d957017fdd75244f5045e829a8441"
checksum = "827e1fb86d24d558ab0454ca3fa084f8a6144ade1e3e6982f697c586bf96b41b"
dependencies = [
"base64",
"memchr",
@ -1406,9 +1406,9 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -1598,9 +1598,9 @@ dependencies = [
"log",
"mac",
"markup5ever",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -2341,9 +2341,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c"
dependencies = [
"migrations_internals",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -2582,9 +2582,9 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -2758,9 +2758,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -2856,9 +2856,9 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -2919,9 +2919,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
"version_check",
]
@ -2931,8 +2931,8 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"version_check",
]
@ -2947,9 +2947,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.39"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
@ -2992,9 +2992,9 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
dependencies = [
"anyhow",
"itertools",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -3037,11 +3037,11 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2 1.0.39",
"proc-macro2 1.0.40",
]
[[package]]
@ -3336,8 +3336,8 @@ checksum = "6f697b8b3f19bee20f30dc87213d05ce091c43bc733ab1bfc98b0e5cdd9943f3"
dependencies = [
"convert_case",
"lazy_static",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"regex",
"tinyjson",
]
@ -3492,9 +3492,9 @@ version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -3538,9 +3538,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
dependencies = [
"darling 0.13.4",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -3561,10 +3561,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2881bccd7d60fb32dfa3d7b3136385312f8ad75e2674aab2852867a09790cae8"
dependencies = [
"proc-macro-error",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"rustversion",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -3661,9 +3661,9 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -3710,8 +3710,8 @@ checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
]
[[package]]
@ -3733,10 +3733,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
dependencies = [
"heck 0.4.0",
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"rustversion",
"syn 1.0.96",
"syn 1.0.98",
]
[[package]]
@ -3752,12 +3752,12 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.96"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"proc-macro2 1.0.40",
"quote 1.0.20",
"unicode-ident",
]
@ -3816,9 +3816,9 @@ version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -3884,9 +3884,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.18.2"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
dependencies = [
"bytes",
"libc",
@ -3919,9 +3919,9 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -4021,10 +4021,10 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757"
dependencies = [
"proc-macro2 1.0.39",
"proc-macro2 1.0.40",
"prost-build",
"quote 1.0.18",
"syn 1.0.96",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -4061,9 +4061,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.34"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if",
"log",
@ -4091,18 +4091,18 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
name = "tracing-core"
version = "0.1.23"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c"
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
dependencies = [
"lazy_static",
"once_cell",
"valuable",
]
@ -4209,9 +4209,9 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
]
[[package]]
@ -4249,9 +4249,9 @@ checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "unicode-normalization"
@ -4397,9 +4397,9 @@ dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
"wasm-bindgen-shared",
]
@ -4421,7 +4421,7 @@ version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
dependencies = [
"quote 1.0.18",
"quote 1.0.20",
"wasm-bindgen-macro-support",
]
@ -4431,9 +4431,9 @@ version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.96",
"proc-macro2 1.0.40",
"quote 1.0.20",
"syn 1.0.98",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View file

@ -7,6 +7,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
local_user::{LocalUser, LocalUserForm},
local_user_language::LocalUserLanguage,
person::{Person, PersonForm},
site::Site,
},
@ -118,6 +119,20 @@ impl Perform for SaveUserSettings {
.await?
.map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
if let Some(discussion_languages) = data.discussion_languages.clone() {
// An empty array is a "clear" / set all languages
let languages = if discussion_languages.is_empty() {
None
} else {
Some(discussion_languages)
};
blocking(context.pool(), move |conn| {
LocalUserLanguage::update_user_languages(conn, languages, local_user_id)
})
.await??;
}
let local_user_form = LocalUserForm {
person_id: Some(person_id),
email,
@ -128,7 +143,7 @@ impl Perform for SaveUserSettings {
theme: data.theme.to_owned(),
default_sort_type,
default_listing_type,
lang: data.lang.to_owned(),
interface_language: data.interface_language.to_owned(),
show_avatars: data.show_avatars,
show_read_posts: data.show_read_posts,
show_new_post_notifs: data.show_new_post_notifs,

View file

@ -6,6 +6,7 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{
source::{
language::Language,
moderator::{ModAdd, ModAddForm},
person::Person,
},
@ -59,6 +60,8 @@ impl Perform for LeaveAdmin {
let federated_instances = build_federated_instances(context.pool(), context.settings()).await?;
let all_languages = blocking(context.pool(), Language::read_all).await??;
Ok(GetSiteResponse {
site_view: Some(site_view),
admins,
@ -66,6 +69,7 @@ impl Perform for LeaveAdmin {
version: version::VERSION.to_string(),
my_user: None,
federated_instances,
all_languages,
})
}
}

View file

@ -34,15 +34,11 @@ impl Perform for Search {
check_private_instance(&local_user_view, context.pool()).await?;
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
let show_bot_accounts = local_user_view
.as_ref()
.map(|t| t.local_user.show_bot_accounts);
let show_read_posts = local_user_view
.as_ref()
.map(|t| t.local_user.show_read_posts);
let person_id = local_user_view.map(|u| u.person.id);
let person_id = local_user_view.as_ref().map(|u| u.person.id);
let mut posts = Vec::new();
let mut comments = Vec::new();
@ -73,9 +69,6 @@ impl Perform for Search {
PostQuery::builder()
.conn(conn)
.sort(sort)
.show_nsfw(show_nsfw)
.show_bot_accounts(show_bot_accounts)
.show_read_posts(show_read_posts)
.listing_type(listing_type)
.community_id(community_id)
.community_actor_id(community_actor_id)
@ -146,9 +139,6 @@ impl Perform for Search {
PostQuery::builder()
.conn(conn)
.sort(sort)
.show_nsfw(show_nsfw)
.show_bot_accounts(show_bot_accounts)
.show_read_posts(show_read_posts)
.listing_type(listing_type)
.community_id(community_id)
.community_actor_id(community_actor_id_2)
@ -226,11 +216,7 @@ impl Perform for Search {
PostQuery::builder()
.conn(conn)
.sort(sort)
.show_nsfw(show_nsfw)
.show_bot_accounts(show_bot_accounts)
.show_read_posts(show_read_posts)
.listing_type(listing_type)
.my_person_id(person_id)
.community_id(community_id)
.community_actor_id(community_actor_id)
.creator_id(creator_id)

View file

@ -14,7 +14,14 @@ pub struct Login {
pub password: Sensitive<String>,
}
use lemmy_db_schema::{
newtypes::{CommentReplyId, CommunityId, PersonId, PersonMentionId, PrivateMessageId},
newtypes::{
CommentReplyId,
CommunityId,
LanguageId,
PersonId,
PersonMentionId,
PrivateMessageId,
},
CommentSortType,
SortType,
};
@ -56,7 +63,7 @@ pub struct SaveUserSettings {
pub theme: Option<String>,
pub default_sort_type: Option<i16>,
pub default_listing_type: Option<i16>,
pub lang: Option<String>,
pub interface_language: Option<String>,
pub avatar: Option<String>,
pub banner: Option<String>,
pub display_name: Option<String>,
@ -69,6 +76,7 @@ pub struct SaveUserSettings {
pub show_bot_accounts: Option<bool>,
pub show_read_posts: Option<bool>,
pub show_new_post_notifs: Option<bool>,
pub discussion_languages: Option<Vec<LanguageId>>,
pub auth: Sensitive<String>,
}

View file

@ -1,6 +1,6 @@
use crate::sensitive::Sensitive;
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, DbUrl, PostId, PostReportId},
newtypes::{CommentId, CommunityId, DbUrl, LanguageId, PostId, PostReportId},
ListingType,
SortType,
};
@ -17,6 +17,7 @@ pub struct CreatePost {
pub body: Option<String>,
pub honeypot: Option<String>,
pub nsfw: Option<bool>,
pub language_id: Option<LanguageId>,
pub auth: Sensitive<String>,
}
@ -71,6 +72,7 @@ pub struct EditPost {
pub url: Option<Url>,
pub body: Option<String>,
pub nsfw: Option<bool>,
pub language_id: Option<LanguageId>,
pub auth: Sensitive<String>,
}

View file

@ -1,6 +1,7 @@
use crate::sensitive::Sensitive;
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, PersonId, PostId},
source::language::Language,
ListingType,
ModlogActionType,
SearchType,
@ -168,6 +169,7 @@ pub struct GetSiteResponse {
pub version: String,
pub my_user: Option<MyUserInfo>,
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
pub all_languages: Vec<Language>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
@ -177,6 +179,7 @@ pub struct MyUserInfo {
pub moderates: Vec<CommunityModeratorView>,
pub community_blocks: Vec<CommunityBlockView>,
pub person_blocks: Vec<PersonBlockView>,
pub discussion_languages: Vec<Language>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]

View file

@ -47,16 +47,14 @@ where
{
let pool = pool.clone();
let blocking_span = tracing::info_span!("blocking operation");
let res = actix_web::web::block(move || {
actix_web::web::block(move || {
let entered = blocking_span.enter();
let conn = pool.get()?;
let res = (f)(&conn);
drop(entered);
Ok(res) as Result<T, LemmyError>
})
.await?;
res
.await?
}
#[tracing::instrument(skip_all)]
@ -407,7 +405,7 @@ pub async fn send_password_reset_email(
.await??;
let email = &user.local_user.email.to_owned().expect("email");
let lang = get_user_lang(user);
let lang = get_interface_language(user);
let subject = &lang.password_reset_subject(&user.person.name);
let protocol_and_hostname = settings.get_protocol_and_hostname();
let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
@ -434,7 +432,7 @@ pub async fn send_verification_email(
);
blocking(pool, move |conn| EmailVerification::create(conn, &form)).await??;
let lang = get_user_lang(user);
let lang = get_interface_language(user);
let subject = lang.verify_email_subject(&settings.hostname);
let body = lang.verify_email_body(&settings.hostname, &user.person.name, verify_link);
send_email(&subject, new_email, &user.person.name, &body, settings)?;
@ -447,14 +445,14 @@ pub fn send_email_verification_success(
settings: &Settings,
) -> Result<(), LemmyError> {
let email = &user.local_user.email.to_owned().expect("email");
let lang = get_user_lang(user);
let lang = get_interface_language(user);
let subject = &lang.email_verified_subject(&user.person.actor_id);
let body = &lang.email_verified_body();
send_email(subject, email, &user.person.name, body, settings)
}
pub fn get_user_lang(user: &LocalUserView) -> Lang {
let user_lang = LanguageId::new(user.local_user.lang.clone());
pub fn get_interface_language(user: &LocalUserView) -> Lang {
let user_lang = LanguageId::new(user.local_user.interface_language.clone());
Lang::from_language_id(&user_lang).unwrap_or_else(|| {
let en = LanguageId::new("en");
Lang::from_language_id(&en).expect("default language")
@ -466,7 +464,7 @@ pub fn send_application_approved_email(
settings: &Settings,
) -> Result<(), LemmyError> {
let email = &user.local_user.email.to_owned().expect("email");
let lang = get_user_lang(user);
let lang = get_interface_language(user);
let subject = lang.registration_approved_subject(&user.person.actor_id);
let body = lang.registration_approved_body(&settings.hostname);
send_email(&subject, email, &user.person.name, &body, settings)
@ -488,7 +486,7 @@ pub async fn check_registration_application(
})
.await??;
if let Some(deny_reason) = registration.deny_reason {
let lang = get_user_lang(local_user_view);
let lang = get_interface_language(local_user_view);
let registration_denied_message = format!("{}: {}", lang.registration_denied(), &deny_reason);
return Err(LemmyError::from_message(&registration_denied_message));
} else {

View file

@ -21,6 +21,7 @@ use lemmy_apub::{
use lemmy_db_schema::{
source::{
community::Community,
language::Language,
post::{Post, PostForm, PostLike, PostLikeForm},
},
traits::{Crud, Likeable},
@ -90,6 +91,15 @@ impl PerformCrud for CreatePost {
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
.unwrap_or_default();
let language_id = Some(
data.language_id.unwrap_or(
blocking(context.pool(), move |conn| {
Language::read_undetermined(conn)
})
.await??,
),
);
let post_form = PostForm {
name: data.name.trim().to_owned(),
url,
@ -100,6 +110,7 @@ impl PerformCrud for CreatePost {
embed_title,
embed_description,
embed_video_url,
language_id,
thumbnail_url: Some(thumbnail_url),
..PostForm::default()
};

View file

@ -32,15 +32,7 @@ impl PerformCrud for GetPosts {
check_private_instance(&local_user_view, context.pool()).await?;
let person_id = local_user_view.to_owned().map(|l| l.person.id);
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
let show_bot_accounts = local_user_view
.as_ref()
.map(|t| t.local_user.show_bot_accounts);
let show_read_posts = local_user_view
.as_ref()
.map(|t| t.local_user.show_read_posts);
let is_logged_in = local_user_view.is_some();
let sort = data.sort;
let listing_type = listing_type_with_site_default(data.type_, context.pool()).await?;
@ -63,13 +55,9 @@ impl PerformCrud for GetPosts {
.conn(conn)
.listing_type(Some(listing_type))
.sort(sort)
.show_nsfw(show_nsfw)
.show_bot_accounts(show_bot_accounts)
.show_read_posts(show_read_posts)
.community_id(community_id)
.community_actor_id(community_actor_id)
.saved_only(saved_only)
.my_person_id(person_id)
.page(page)
.limit(limit)
.build()
@ -79,7 +67,7 @@ impl PerformCrud for GetPosts {
.map_err(|e| LemmyError::from_error_message(e, "couldnt_get_posts"))?;
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() {
if !is_logged_in {
for pv in posts
.iter_mut()
.filter(|p| p.post.deleted || p.post.removed)

View file

@ -14,7 +14,10 @@ use lemmy_apub::protocol::activities::{
CreateOrUpdateType,
};
use lemmy_db_schema::{
source::post::{Post, PostForm},
source::{
language::Language,
post::{Post, PostForm},
},
traits::Crud,
utils::{diesel_option_overwrite, naive_now},
};
@ -82,6 +85,15 @@ impl PerformCrud for EditPost {
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
.unwrap_or_default();
let language_id = Some(
data.language_id.unwrap_or(
blocking(context.pool(), move |conn| {
Language::read_undetermined(conn)
})
.await??,
),
);
let post_form = PostForm {
creator_id: orig_post.creator_id.to_owned(),
community_id: orig_post.community_id,
@ -93,6 +105,7 @@ impl PerformCrud for EditPost {
embed_title,
embed_description,
embed_video_url,
language_id,
thumbnail_url: Some(thumbnail_url),
..PostForm::default()
};

View file

@ -5,8 +5,8 @@ use lemmy_api_common::{
utils::{
blocking,
check_person_block,
get_interface_language,
get_local_user_view_from_jwt,
get_user_lang,
send_email_to_user,
},
};
@ -109,7 +109,7 @@ impl PerformCrud for CreatePrivateMessage {
LocalUserView::read_person(conn, recipient_id)
})
.await??;
let lang = get_user_lang(&local_recipient);
let lang = get_interface_language(&local_recipient);
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
send_email_to_user(
&local_recipient,

View file

@ -5,7 +5,8 @@ use lemmy_api_common::{
site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo},
utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt},
};
use lemmy_db_views::structs::SiteView;
use lemmy_db_schema::source::language::Language;
use lemmy_db_views::structs::{LocalUserDiscussionLanguageView, SiteView};
use lemmy_db_views_actor::structs::{
CommunityBlockView,
CommunityFollowerView,
@ -83,6 +84,8 @@ impl PerformCrud for GetSite {
.await?
{
let person_id = local_user_view.person.id;
let local_user_id = local_user_view.local_user.id;
let follows = blocking(context.pool(), move |conn| {
CommunityFollowerView::for_person(conn, person_id)
})
@ -109,12 +112,19 @@ impl PerformCrud for GetSite {
.await?
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?;
let discussion_languages = blocking(context.pool(), move |conn| {
LocalUserDiscussionLanguageView::read_languages(conn, local_user_id)
})
.await?
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?;
Some(MyUserInfo {
local_user_view,
follows,
moderates,
community_blocks,
person_blocks,
discussion_languages,
})
} else {
None
@ -122,6 +132,8 @@ impl PerformCrud for GetSite {
let federated_instances = build_federated_instances(context.pool(), context.settings()).await?;
let all_languages = blocking(context.pool(), Language::read_all).await??;
Ok(GetSiteResponse {
site_view,
admins,
@ -129,6 +141,7 @@ impl PerformCrud for GetSite {
version: version::VERSION.to_string(),
my_user,
federated_instances,
all_languages,
})
}
}

View file

@ -15,6 +15,7 @@ use lemmy_db_schema::{
aggregates::structs::PersonAggregates,
source::{
local_user::{LocalUser, LocalUserForm},
local_user_language::LocalUserLanguage,
person::{Person, PersonForm},
registration_application::{RegistrationApplication, RegistrationApplicationForm},
site::Site,
@ -167,10 +168,17 @@ impl PerformCrud for Register {
}
};
// Update the users languages to all by default
let local_user_id = inserted_local_user.id;
blocking(context.pool(), move |conn| {
LocalUserLanguage::update_user_languages(conn, None, local_user_id)
})
.await??;
if require_application {
// Create the registration application
let form = RegistrationApplicationForm {
local_user_id: Some(inserted_local_user.id),
local_user_id: Some(local_user_id),
// We already made sure answer was not null above
answer: data.answer.to_owned(),
..RegistrationApplicationForm::default()

View file

@ -34,13 +34,9 @@ impl PerformCrud for GetPersonDetails {
check_private_instance(&local_user_view, context.pool()).await?;
let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
let show_bot_accounts = local_user_view
.as_ref()
.map(|t| t.local_user.show_bot_accounts);
let show_read_posts = local_user_view
.as_ref()
.map(|t| t.local_user.show_read_posts);
let person_details_id = match data.person_id {
Some(id) => id,
@ -58,8 +54,6 @@ impl PerformCrud for GetPersonDetails {
}
};
let person_id = local_user_view.map(|uv| uv.person.id);
// You don't need to return settings for the user, since this comes back with GetSite
// `my_user`
let person_view = blocking(context.pool(), move |conn| {
@ -77,15 +71,12 @@ impl PerformCrud for GetPersonDetails {
let posts_query = PostQuery::builder()
.conn(conn)
.sort(sort)
.show_nsfw(show_nsfw)
.show_bot_accounts(show_bot_accounts)
.show_read_posts(show_read_posts)
.saved_only(saved_only)
.community_id(community_id)
.my_person_id(person_id)
.page(page)
.limit(limit);
let person_id = local_user_view.map(|uv| uv.person.id);
let comments_query = CommentQuery::builder()
.conn(conn)
.my_person_id(person_id)

View file

@ -28,6 +28,10 @@
"commentsEnabled": true,
"sensitive": false,
"stickied": false,
"language": {
"identifier": "ko",
"name": "한국어"
},
"published": "2021-10-29T15:10:51.557399+00:00"
},
"cc": [

View file

@ -27,5 +27,9 @@
"sensitive": false,
"commentsEnabled": true,
"stickied": true,
"language": {
"identifier": "fr",
"name": "Français"
},
"published": "2021-02-26T12:35:34.292626+00:00"
}

View file

@ -40,7 +40,7 @@ fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
.debug(context.settings().federation.debug)
// TODO No idea why, but you can't pass context.settings() to the verify_url_function closure
// without the value getting captured.
.verify_url_function(|url| check_apub_id_valid(url, &SETTINGS.to_owned()))
.verify_url_function(|url| check_apub_id_valid(url, &SETTINGS))
.build()
.expect("configure federation");
LocalInstance::new(

View file

@ -4,7 +4,7 @@ use crate::{
local_instance,
objects::{read_from_string_or_source_opt, verify_is_remote_object},
protocol::{
objects::page::{Attachment, AttributedTo, Page, PageType},
objects::page::{Attachment, AttributedTo, LanguageTag, Page, PageType},
ImageObject,
Source,
},
@ -22,6 +22,7 @@ use lemmy_db_schema::{
self,
source::{
community::Community,
language::Language,
moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm},
person::Person,
post::{Post, PostForm},
@ -98,6 +99,11 @@ impl ApubObject for ApubPost {
Community::read(conn, community_id)
})
.await??;
let language = self.language_id;
let language = blocking(context.pool(), move |conn| {
Language::read_from_id(conn, language)
})
.await??;
let page = Page {
kind: PageType::Page,
@ -115,6 +121,7 @@ impl ApubObject for ApubPost {
comments_enabled: Some(!self.locked),
sensitive: Some(self.nsfw),
stickied: Some(self.stickied),
language: LanguageTag::new(language),
published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
};
@ -181,6 +188,11 @@ impl ApubObject for ApubPost {
let body_slurs_removed =
read_from_string_or_source_opt(&page.content, &page.media_type, &page.source)
.map(|s| Some(remove_slurs(&s, &context.settings().slur_regex())));
let language = page.language.map(|l| l.identifier);
let language = blocking(context.pool(), move |conn| {
Language::read_id_from_code_opt(conn, language.as_deref())
})
.await??;
PostForm {
name: page.name.clone(),
@ -201,6 +213,7 @@ impl ApubObject for ApubPost {
thumbnail_url: Some(thumbnail_url),
ap_id: Some(page.id.clone().into()),
local: Some(false),
language_id: language,
}
} else {
// if is mod action, only update locked/stickied fields, nothing else

View file

@ -16,7 +16,7 @@ use activitypub_federation::{
use activitystreams_kinds::{link::LinkType, object::ImageType};
use chrono::{DateTime, FixedOffset};
use itertools::Itertools;
use lemmy_db_schema::newtypes::DbUrl;
use lemmy_db_schema::{newtypes::DbUrl, source::language::Language};
use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
@ -62,8 +62,29 @@ pub struct Page {
pub(crate) stickied: Option<bool>,
pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>,
pub(crate) language: Option<LanguageTag>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct LanguageTag {
pub(crate) identifier: String,
pub(crate) name: String,
}
impl LanguageTag {
pub(crate) fn new(lang: Language) -> Option<LanguageTag> {
// undetermined
if lang.code == "und" {
None
} else {
Some(LanguageTag {
identifier: lang.code,
name: lang.name,
})
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Link {

View file

@ -0,0 +1,54 @@
use crate::{newtypes::LanguageId, source::language::Language};
use diesel::{result::Error, PgConnection, RunQueryDsl, *};
impl Language {
pub fn read_all(conn: &PgConnection) -> Result<Vec<Language>, Error> {
use crate::schema::language::dsl::*;
language.load::<Self>(conn)
}
pub fn read_from_id(conn: &PgConnection, id_: LanguageId) -> Result<Language, Error> {
use crate::schema::language::dsl::*;
language.filter(id.eq(id_)).first::<Self>(conn)
}
pub fn read_id_from_code(conn: &PgConnection, code_: &str) -> Result<LanguageId, Error> {
use crate::schema::language::dsl::*;
Ok(language.filter(code.eq(code_)).first::<Self>(conn)?.id)
}
pub fn read_id_from_code_opt(
conn: &PgConnection,
code_: Option<&str>,
) -> Result<Option<LanguageId>, Error> {
if let Some(code_) = code_ {
Ok(Some(Language::read_id_from_code(conn, code_)?))
} else {
Ok(None)
}
}
pub fn read_undetermined(conn: &PgConnection) -> Result<LanguageId, Error> {
use crate::schema::language::dsl::*;
Ok(language.filter(code.eq("und")).first::<Self>(conn)?.id)
}
}
#[cfg(test)]
mod tests {
use crate::{source::language::Language, utils::establish_unpooled_connection};
use serial_test::serial;
#[test]
#[serial]
fn test_languages() {
let conn = establish_unpooled_connection();
let all = Language::read_all(&conn).unwrap();
assert_eq!(184, all.len());
assert_eq!("ak", all[5].code);
assert_eq!("lv", all[99].code);
assert_eq!("yi", all[179].code);
}
}

View file

@ -23,7 +23,7 @@ mod safe_settings_type {
theme,
default_sort_type,
default_listing_type,
lang,
interface_language,
show_avatars,
send_notifications_to_email,
validator_time,
@ -48,7 +48,7 @@ mod safe_settings_type {
theme,
default_sort_type,
default_listing_type,
lang,
interface_language,
show_avatars,
send_notifications_to_email,
validator_time,

View file

@ -0,0 +1,42 @@
use crate::{
newtypes::{LanguageId, LocalUserId},
source::{language::Language, local_user_language::*},
};
use diesel::{result::Error, PgConnection, RunQueryDsl, *};
impl LocalUserLanguage {
/// Update the user's languages.
///
/// If no language_id vector is given, it will show all languages
pub fn update_user_languages(
conn: &PgConnection,
language_ids: Option<Vec<LanguageId>>,
for_local_user_id: LocalUserId,
) -> Result<(), Error> {
use crate::schema::local_user_language::dsl::*;
// If no language is given, read all languages
let lang_ids = language_ids.unwrap_or(
Language::read_all(conn)?
.into_iter()
.map(|l| l.id)
.collect(),
);
conn.build_transaction().read_write().run(|| {
// Clear the current user languages
delete(local_user_language.filter(local_user_id.eq(for_local_user_id))).execute(conn)?;
for l in lang_ids {
let form = LocalUserLanguageForm {
local_user_id: for_local_user_id,
language_id: l,
};
insert_into(local_user_language)
.values(form)
.get_result::<Self>(conn)?;
}
Ok(())
})
}
}

View file

@ -5,7 +5,9 @@ pub mod comment_report;
pub mod community;
pub mod community_block;
pub mod email_verification;
pub mod language;
pub mod local_user;
pub mod local_user_language;
pub mod moderator;
pub mod password_reset_request;
pub mod person;

View file

@ -387,6 +387,7 @@ mod tests {
thumbnail_url: None,
ap_id: inserted_post.ap_id.to_owned(),
local: true,
language_id: Default::default(),
};
// Post Like

View file

@ -69,6 +69,14 @@ pub struct CommentReportId(i32);
#[cfg_attr(feature = "full", derive(DieselNewType))]
pub struct PostReportId(i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "full", derive(DieselNewType))]
pub struct LanguageId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "full", derive(DieselNewType))]
pub struct LocalUserLanguageId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "full", derive(DieselNewType))]
pub struct CommentReplyId(i32);

View file

@ -156,7 +156,7 @@ table! {
theme -> Varchar,
default_sort_type -> Int2,
default_listing_type -> Int2,
lang -> Varchar,
interface_language -> Varchar,
show_avatars -> Bool,
send_notifications_to_email -> Bool,
validator_time -> Timestamp,
@ -375,6 +375,7 @@ table! {
thumbnail_url -> Nullable<Text>,
ap_id -> Varchar,
local -> Bool,
language_id -> Int4,
}
}
@ -645,6 +646,22 @@ table! {
}
}
table! {
language (id) {
id -> Int4,
code -> Text,
name -> Text,
}
}
table! {
local_user_language(id) {
id -> Int4,
local_user_id -> Int4,
language_id -> Int4,
}
}
joinable!(person_mention -> person_alias_1 (recipient_id));
joinable!(comment_reply -> person_alias_1 (recipient_id));
joinable!(post -> person_alias_1 (creator_id));
@ -711,6 +728,9 @@ joinable!(registration_application -> local_user (local_user_id));
joinable!(registration_application -> person (admin_id));
joinable!(mod_hide_community -> person (mod_person_id));
joinable!(mod_hide_community -> community (community_id));
joinable!(post -> language (language_id));
joinable!(local_user_language -> language (language_id));
joinable!(local_user_language -> local_user (local_user_id));
joinable!(admin_purge_comment -> person (admin_person_id));
joinable!(admin_purge_comment -> post (post_id));
@ -767,5 +787,7 @@ allow_tables_to_appear_in_same_query!(
admin_purge_person,
admin_purge_post,
email_verification,
registration_application
registration_application,
language,
local_user_language
);

View file

@ -0,0 +1,15 @@
use crate::newtypes::LanguageId;
use serde::{Deserialize, Serialize};
#[cfg(feature = "full")]
use crate::schema::language;
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
#[cfg_attr(feature = "full", table_name = "language")]
pub struct Language {
#[serde(skip)]
pub id: LanguageId,
pub code: String,
pub name: String,
}

View file

@ -16,7 +16,7 @@ pub struct LocalUser {
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub interface_language: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub validator_time: chrono::NaiveDateTime,
@ -40,7 +40,7 @@ pub struct LocalUserForm {
pub theme: Option<String>,
pub default_sort_type: Option<i16>,
pub default_listing_type: Option<i16>,
pub lang: Option<String>,
pub interface_language: Option<String>,
pub show_avatars: Option<bool>,
pub send_notifications_to_email: Option<bool>,
pub show_bot_accounts: Option<bool>,
@ -63,7 +63,7 @@ pub struct LocalUserSettings {
pub theme: String,
pub default_sort_type: i16,
pub default_listing_type: i16,
pub lang: String,
pub interface_language: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub validator_time: chrono::NaiveDateTime,

View file

@ -0,0 +1,23 @@
use crate::newtypes::{LanguageId, LocalUserId, LocalUserLanguageId};
use serde::{Deserialize, Serialize};
#[cfg(feature = "full")]
use crate::schema::local_user_language;
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
#[cfg_attr(feature = "full", table_name = "local_user_language")]
pub struct LocalUserLanguage {
#[serde(skip)]
pub id: LocalUserLanguageId,
pub local_user_id: LocalUserId,
pub language_id: LanguageId,
}
#[derive(Clone)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", table_name = "local_user_language")]
pub struct LocalUserLanguageForm {
pub local_user_id: LocalUserId,
pub language_id: LanguageId,
}

View file

@ -6,7 +6,9 @@ pub mod comment_report;
pub mod community;
pub mod community_block;
pub mod email_verification;
pub mod language;
pub mod local_user;
pub mod local_user_language;
pub mod moderator;
pub mod password_reset_request;
pub mod person;

View file

@ -1,4 +1,4 @@
use crate::newtypes::{CommunityId, DbUrl, PersonId, PostId};
use crate::newtypes::{CommunityId, DbUrl, LanguageId, PersonId, PostId};
use serde::{Deserialize, Serialize};
#[cfg(feature = "full")]
@ -27,6 +27,7 @@ pub struct Post {
pub thumbnail_url: Option<DbUrl>,
pub ap_id: DbUrl,
pub local: bool,
pub language_id: LanguageId,
}
#[derive(Default)]
@ -51,6 +52,7 @@ pub struct PostForm {
pub thumbnail_url: Option<Option<DbUrl>>,
pub ap_id: Option<DbUrl>,
pub local: Option<bool>,
pub language_id: Option<LanguageId>,
}
#[derive(PartialEq, Debug)]

View file

@ -574,6 +574,7 @@ mod tests {
thumbnail_url: None,
ap_id: inserted_post.ap_id.to_owned(),
local: true,
language_id: Default::default(),
},
community: CommunitySafe {
id: inserted_community.id,

View file

@ -6,6 +6,8 @@ pub mod comment_report_view;
#[cfg(feature = "full")]
pub mod comment_view;
#[cfg(feature = "full")]
pub mod local_user_discussion_language_view;
#[cfg(feature = "full")]
pub mod local_user_view;
#[cfg(feature = "full")]
pub mod post_report_view;

View file

@ -0,0 +1,32 @@
use crate::structs::LocalUserDiscussionLanguageView;
use diesel::{result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_db_schema::{
newtypes::LocalUserId,
schema::{language, local_user, local_user_language},
source::{
language::Language,
local_user::{LocalUser, LocalUserSettings},
},
traits::ToSafeSettings,
};
type LocalUserDiscussionLanguageViewTuple = (LocalUserSettings, Language);
impl LocalUserDiscussionLanguageView {
pub fn read_languages(
conn: &PgConnection,
local_user_id: LocalUserId,
) -> Result<Vec<Language>, Error> {
let res = local_user_language::table
.inner_join(local_user::table)
.inner_join(language::table)
.select((
LocalUser::safe_settings_columns_tuple(),
language::all_columns,
))
.filter(local_user::id.eq(local_user_id))
.load::<LocalUserDiscussionLanguageViewTuple>(conn)?;
Ok(res.into_iter().map(|a| a.1).collect::<Vec<Language>>())
}
}

View file

@ -2,12 +2,14 @@ use crate::structs::PostView;
use diesel::{dsl::*, pg::Pg, result::Error, *};
use lemmy_db_schema::{
aggregates::structs::PostAggregates,
newtypes::{CommunityId, DbUrl, PersonId, PostId},
newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
schema::{
community,
community_block,
community_follower,
community_person_ban,
language,
local_user_language,
person,
person_block,
post,
@ -18,6 +20,7 @@ use lemmy_db_schema::{
},
source::{
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
language::Language,
person::{Person, PersonSafe},
person_block::PersonBlock,
post::{Post, PostRead, PostSaved},
@ -41,6 +44,7 @@ type PostViewTuple = (
Option<PostRead>,
Option<PersonBlock>,
Option<i16>,
Language,
);
impl PostView {
@ -51,7 +55,6 @@ impl PostView {
) -> Result<Self, Error> {
// The left join below will return None in this case
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
let (
post,
creator,
@ -63,6 +66,7 @@ impl PostView {
read,
creator_blocked,
post_like,
language,
) = post::table
.find(post_id)
.inner_join(person::table)
@ -115,6 +119,7 @@ impl PostView {
.and(post_like::person_id.eq(person_id_join)),
),
)
.inner_join(language::table)
.select((
post::all_columns,
Person::safe_columns_tuple(),
@ -126,6 +131,7 @@ impl PostView {
post_read::all_columns.nullable(),
person_block::all_columns.nullable(),
post_like::score.nullable(),
language::all_columns,
))
.first::<PostViewTuple>(conn)?;
@ -148,6 +154,7 @@ impl PostView {
read: read.is_some(),
creator_blocked: creator_blocked.is_some(),
my_vote,
language,
})
}
}
@ -163,6 +170,7 @@ pub struct PostQuery<'a> {
community_id: Option<CommunityId>,
community_actor_id: Option<DbUrl>,
my_person_id: Option<PersonId>,
my_local_user_id: Option<LocalUserId>,
search_term: Option<String>,
url_search: Option<String>,
show_nsfw: Option<bool>,
@ -179,6 +187,7 @@ impl<'a> PostQuery<'a> {
// The left join below will return None in this case
let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
let local_user_id_join = self.my_local_user_id.unwrap_or(LocalUserId(-1));
let mut query = post::table
.inner_join(person::table)
@ -238,6 +247,14 @@ impl<'a> PostQuery<'a> {
.and(post_like::person_id.eq(person_id_join)),
),
)
.inner_join(language::table)
.left_join(
local_user_language::table.on(
post::language_id
.eq(local_user_language::language_id)
.and(local_user_language::local_user_id.eq(local_user_id_join)),
),
)
.select((
post::all_columns,
Person::safe_columns_tuple(),
@ -249,6 +266,7 @@ impl<'a> PostQuery<'a> {
post_read::all_columns.nullable(),
person_block::all_columns.nullable(),
post_like::score.nullable(),
language::all_columns,
))
.into_boxed();
@ -323,6 +341,11 @@ impl<'a> PostQuery<'a> {
query = query.filter(post_read::id.is_null());
}
// Filter out the rows with missing languages
if self.my_local_user_id.is_some() {
query = query.filter(local_user_language::id.is_not_null());
}
// Don't show blocked communities or persons
if self.my_person_id.is_some() {
query = query.filter(community_block::person_id.is_null());
@ -403,6 +426,7 @@ impl ViewToVec for PostView {
read: a.7.is_some(),
creator_blocked: a.8.is_some(),
my_vote: a.9,
language: a.10,
})
.collect::<Vec<Self>>()
}
@ -411,11 +435,16 @@ impl ViewToVec for PostView {
#[cfg(test)]
mod tests {
use crate::post_view::{PostQuery, PostView};
use diesel::PgConnection;
use lemmy_db_schema::{
aggregates::structs::PostAggregates,
newtypes::LanguageId,
source::{
community::*,
community_block::{CommunityBlock, CommunityBlockForm},
language::Language,
local_user::{LocalUser, LocalUserForm},
local_user_language::LocalUserLanguage,
person::*,
person_block::{PersonBlock, PersonBlockForm},
post::*,
@ -427,15 +456,16 @@ mod tests {
};
use serial_test::serial;
#[test]
#[serial]
fn test_crud() {
let conn = establish_unpooled_connection();
struct Data {
inserted_person: Person,
inserted_blocked_person: Person,
inserted_bot: Person,
inserted_community: Community,
inserted_post: Post,
}
fn init_data(conn: &PgConnection) -> Data {
let person_name = "tegan".to_string();
let community_name = "test_community_3".to_string();
let post_name = "test post 3".to_string();
let bot_post_name = "test bot post".to_string();
let new_person = PersonForm {
name: person_name.to_owned(),
@ -443,43 +473,44 @@ mod tests {
..PersonForm::default()
};
let inserted_person = Person::create(&conn, &new_person).unwrap();
let inserted_person = Person::create(conn, &new_person).unwrap();
let new_bot = PersonForm {
name: person_name.to_owned(),
name: "mybot".to_string(),
bot_account: Some(true),
public_key: Some("pubkey".to_string()),
..PersonForm::default()
};
let inserted_bot = Person::create(&conn, &new_bot).unwrap();
let inserted_bot = Person::create(conn, &new_bot).unwrap();
let new_community = CommunityForm {
name: community_name.to_owned(),
name: "test_community_3".to_string(),
title: "nada".to_owned(),
public_key: Some("pubkey".to_string()),
..CommunityForm::default()
};
let inserted_community = Community::create(&conn, &new_community).unwrap();
let inserted_community = Community::create(conn, &new_community).unwrap();
// Test a person block, make sure the post query doesn't include their post
let blocked_person = PersonForm {
name: person_name.to_owned(),
name: person_name,
public_key: Some("pubkey".to_string()),
..PersonForm::default()
};
let inserted_blocked_person = Person::create(&conn, &blocked_person).unwrap();
let inserted_blocked_person = Person::create(conn, &blocked_person).unwrap();
let post_from_blocked_person = PostForm {
name: "blocked_person_post".to_string(),
creator_id: inserted_blocked_person.id,
community_id: inserted_community.id,
language_id: Some(LanguageId(1)),
..PostForm::default()
};
Post::create(&conn, &post_from_blocked_person).unwrap();
Post::create(conn, &post_from_blocked_person).unwrap();
// block that person
let person_block = PersonBlockForm {
@ -487,72 +518,58 @@ mod tests {
target_id: inserted_blocked_person.id,
};
PersonBlock::block(&conn, &person_block).unwrap();
PersonBlock::block(conn, &person_block).unwrap();
// A sample post
let new_post = PostForm {
name: post_name.to_owned(),
name: "test post 3".to_string(),
creator_id: inserted_person.id,
community_id: inserted_community.id,
language_id: Some(LanguageId(47)),
..PostForm::default()
};
let inserted_post = Post::create(&conn, &new_post).unwrap();
let inserted_post = Post::create(conn, &new_post).unwrap();
let new_bot_post = PostForm {
name: bot_post_name,
name: "test bot post".to_string(),
creator_id: inserted_bot.id,
community_id: inserted_community.id,
..PostForm::default()
};
let _inserted_bot_post = Post::create(&conn, &new_bot_post).unwrap();
let _inserted_bot_post = Post::create(conn, &new_bot_post).unwrap();
let post_like_form = PostLikeForm {
post_id: inserted_post.id,
person_id: inserted_person.id,
score: 1,
};
Data {
inserted_person,
inserted_blocked_person,
inserted_bot,
inserted_community,
inserted_post,
}
}
let inserted_post_like = PostLike::like(&conn, &post_like_form).unwrap();
fn cleanup(data: Data, conn: &PgConnection) {
let num_deleted = Post::delete(conn, data.inserted_post.id).unwrap();
Community::delete(conn, data.inserted_community.id).unwrap();
Person::delete(conn, data.inserted_person.id).unwrap();
Person::delete(conn, data.inserted_bot.id).unwrap();
Person::delete(conn, data.inserted_blocked_person.id).unwrap();
assert_eq!(1, num_deleted);
}
let expected_post_like = PostLike {
id: inserted_post_like.id,
post_id: inserted_post.id,
person_id: inserted_person.id,
published: inserted_post_like.published,
score: 1,
};
fn expected_post_listing(data: &Data, conn: &PgConnection) -> PostView {
let (inserted_person, inserted_community, inserted_post) = (
&data.inserted_person,
&data.inserted_community,
&data.inserted_post,
);
let agg = PostAggregates::read(conn, inserted_post.id).unwrap();
let read_post_listings_with_person = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.show_bot_accounts(Some(false))
.community_id(Some(inserted_community.id))
.my_person_id(Some(inserted_person.id))
.build()
.list()
.unwrap();
let read_post_listings_no_person = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.community_id(Some(inserted_community.id))
.build()
.list()
.unwrap();
let read_post_listing_no_person = PostView::read(&conn, inserted_post.id, None).unwrap();
let read_post_listing_with_person =
PostView::read(&conn, inserted_post.id, Some(inserted_person.id)).unwrap();
let agg = PostAggregates::read(&conn, inserted_post.id).unwrap();
// the non person version
let expected_post_listing_no_person = PostView {
PostView {
post: Post {
id: inserted_post.id,
name: post_name,
name: inserted_post.name.clone(),
creator_id: inserted_person.id,
url: None,
body: None,
@ -570,11 +587,12 @@ mod tests {
thumbnail_url: None,
ap_id: inserted_post.ap_id.to_owned(),
local: true,
language_id: LanguageId(47),
},
my_vote: None,
creator: PersonSafe {
id: inserted_person.id,
name: person_name,
name: inserted_person.name.clone(),
display_name: None,
published: inserted_person.published,
avatar: None,
@ -595,7 +613,7 @@ mod tests {
creator_banned_from_community: false,
community: CommunitySafe {
id: inserted_community.id,
name: community_name,
name: inserted_community.name.clone(),
icon: None,
removed: false,
deleted: false,
@ -614,8 +632,8 @@ mod tests {
id: agg.id,
post_id: inserted_post.id,
comments: 0,
score: 1,
upvotes: 1,
score: 0,
upvotes: 0,
downvotes: 0,
stickied: false,
published: agg.published,
@ -626,66 +644,239 @@ mod tests {
read: false,
saved: false,
creator_blocked: false,
};
language: Language {
id: LanguageId(47),
code: "fr".to_string(),
name: "Français".to_string(),
},
}
}
#[test]
#[serial]
fn post_listing_with_person() {
let conn = establish_unpooled_connection();
let data = init_data(&conn);
let read_post_listing = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.community_id(Some(data.inserted_community.id))
.show_bot_accounts(Some(false))
.my_person_id(Some(data.inserted_person.id))
.build()
.list()
.unwrap();
let post_listing_single_with_person =
PostView::read(&conn, data.inserted_post.id, Some(data.inserted_person.id)).unwrap();
let mut expected_post_listing_with_user = expected_post_listing(&data, &conn);
// Should be only one person, IE the bot post, and blocked should be missing
assert_eq!(1, read_post_listing.len());
assert_eq!(expected_post_listing_with_user, read_post_listing[0]);
expected_post_listing_with_user.my_vote = Some(0);
assert_eq!(
expected_post_listing_with_user,
post_listing_single_with_person
);
let post_listings_with_bots = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.community_id(Some(data.inserted_community.id))
.show_bot_accounts(Some(true))
.my_person_id(Some(data.inserted_person.id))
.build()
.list()
.unwrap();
// should include bot post which has "undetermined" language
assert_eq!(2, post_listings_with_bots.len());
cleanup(data, &conn);
}
#[test]
#[serial]
fn post_listing_no_person() {
let conn = establish_unpooled_connection();
let data = init_data(&conn);
let read_post_listing_multiple_no_person = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.community_id(Some(data.inserted_community.id))
.build()
.list()
.unwrap();
let read_post_listing_single_no_person =
PostView::read(&conn, data.inserted_post.id, None).unwrap();
let expected_post_listing_no_person = expected_post_listing(&data, &conn);
// Should be 2 posts, with the bot post, and the blocked
assert_eq!(3, read_post_listing_multiple_no_person.len());
assert_eq!(
expected_post_listing_no_person,
read_post_listing_multiple_no_person[1]
);
assert_eq!(
expected_post_listing_no_person,
read_post_listing_single_no_person
);
cleanup(data, &conn);
}
#[test]
#[serial]
fn post_listing_block_community() {
let conn = establish_unpooled_connection();
let data = init_data(&conn);
// Test a community block
let community_block = CommunityBlockForm {
person_id: inserted_person.id,
community_id: inserted_community.id,
person_id: data.inserted_person.id,
community_id: data.inserted_community.id,
};
CommunityBlock::block(&conn, &community_block).unwrap();
let read_post_listings_with_person_after_block = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.show_bot_accounts(Some(false))
.community_id(Some(inserted_community.id))
.my_person_id(Some(inserted_person.id))
.community_id(Some(data.inserted_community.id))
.show_bot_accounts(Some(true))
.my_person_id(Some(data.inserted_person.id))
.build()
.list()
.unwrap();
// Should be 0 posts after the community block
assert_eq!(0, read_post_listings_with_person_after_block.len());
CommunityBlock::unblock(&conn, &community_block).unwrap();
cleanup(data, &conn);
}
#[test]
#[serial]
fn post_listing_like() {
let conn = establish_unpooled_connection();
let data = init_data(&conn);
let post_like_form = PostLikeForm {
post_id: data.inserted_post.id,
person_id: data.inserted_person.id,
score: 1,
};
let inserted_post_like = PostLike::like(&conn, &post_like_form).unwrap();
let expected_post_like = PostLike {
id: inserted_post_like.id,
post_id: data.inserted_post.id,
person_id: data.inserted_person.id,
published: inserted_post_like.published,
score: 1,
};
assert_eq!(expected_post_like, inserted_post_like);
let like_removed =
PostLike::remove(&conn, data.inserted_person.id, data.inserted_post.id).unwrap();
assert_eq!(1, like_removed);
cleanup(data, &conn);
}
#[test]
#[serial]
fn post_listing_person_language() {
let conn = establish_unpooled_connection();
let data = init_data(&conn);
let spanish_id = Language::read_id_from_code(&conn, "es").unwrap();
let post_spanish = PostForm {
name: "asffgdsc".to_string(),
creator_id: data.inserted_person.id,
community_id: data.inserted_community.id,
language_id: Some(spanish_id),
..PostForm::default()
};
Post::create(&conn, &post_spanish).unwrap();
let my_person_form = PersonForm {
name: "Reverie Toiba".to_string(),
public_key: Some("pubkey".to_string()),
..PersonForm::default()
};
let my_person = Person::create(&conn, &my_person_form).unwrap();
let local_user_form = LocalUserForm {
person_id: Some(my_person.id),
password_encrypted: Some("".to_string()),
..Default::default()
};
let local_user = LocalUser::create(&conn, &local_user_form).unwrap();
// Update the users languages to all
LocalUserLanguage::update_user_languages(&conn, None, local_user.id).unwrap();
let post_listings_all = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.show_bot_accounts(Some(true))
.my_person_id(Some(my_person.id))
.my_local_user_id(Some(local_user.id))
.build()
.list()
.unwrap();
// TODO More needs to be added here
let mut expected_post_listing_with_user = expected_post_listing_no_person.to_owned();
expected_post_listing_with_user.my_vote = Some(1);
// no language filters specified, all posts should be returned
assert_eq!(4, post_listings_all.len());
let like_removed = PostLike::remove(&conn, inserted_person.id, inserted_post.id).unwrap();
let num_deleted = Post::delete(&conn, inserted_post.id).unwrap();
PersonBlock::unblock(&conn, &person_block).unwrap();
CommunityBlock::unblock(&conn, &community_block).unwrap();
Community::delete(&conn, inserted_community.id).unwrap();
Person::delete(&conn, inserted_person.id).unwrap();
Person::delete(&conn, inserted_bot.id).unwrap();
Person::delete(&conn, inserted_blocked_person.id).unwrap();
let french_id = Language::read_id_from_code(&conn, "fr").unwrap();
LocalUserLanguage::update_user_languages(&conn, Some(vec![french_id]), local_user.id).unwrap();
// The with user
let post_listing_french = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.show_bot_accounts(Some(true))
.my_person_id(Some(my_person.id))
.my_local_user_id(Some(local_user.id))
.build()
.list()
.unwrap();
// only one french language post should be returned
assert_eq!(1, post_listing_french.len());
assert_eq!(french_id, post_listing_french[0].post.language_id);
let undetermined_id = Language::read_id_from_code(&conn, "und").unwrap();
LocalUserLanguage::update_user_languages(
&conn,
Some(vec![french_id, undetermined_id]),
local_user.id,
)
.unwrap();
let post_listings_french_und = PostQuery::builder()
.conn(&conn)
.sort(Some(SortType::New))
.show_bot_accounts(Some(true))
.my_person_id(Some(my_person.id))
.my_local_user_id(Some(local_user.id))
.build()
.list()
.unwrap();
// french post and undetermined language post should be returned
assert_eq!(2, post_listings_french_und.len());
assert_eq!(
expected_post_listing_with_user,
read_post_listings_with_person[0]
);
assert_eq!(
expected_post_listing_with_user,
read_post_listing_with_person
undetermined_id,
post_listings_french_und[0].post.language_id
);
assert_eq!(french_id, post_listings_french_und[1].post.language_id);
// Should be only one person, IE the bot post, and blocked should be missing
assert_eq!(1, read_post_listings_with_person.len());
// Without the user
assert_eq!(
expected_post_listing_no_person,
read_post_listings_no_person[1]
);
assert_eq!(expected_post_listing_no_person, read_post_listing_no_person);
// Should be 2 posts, with the bot post, and the blocked
assert_eq!(3, read_post_listings_no_person.len());
// Should be 0 posts after the community block
assert_eq!(0, read_post_listings_with_person_after_block.len());
assert_eq!(expected_post_like, inserted_post_like);
assert_eq!(1, like_removed);
assert_eq!(1, num_deleted);
cleanup(data, &conn);
}
}

View file

@ -239,7 +239,7 @@ mod tests {
theme: inserted_sara_local_user.theme,
default_sort_type: inserted_sara_local_user.default_sort_type,
default_listing_type: inserted_sara_local_user.default_listing_type,
lang: inserted_sara_local_user.lang,
interface_language: inserted_sara_local_user.interface_language,
show_avatars: inserted_sara_local_user.show_avatars,
send_notifications_to_email: inserted_sara_local_user.send_notifications_to_email,
validator_time: inserted_sara_local_user.validator_time,

View file

@ -4,6 +4,7 @@ use lemmy_db_schema::{
comment::Comment,
comment_report::CommentReport,
community::CommunitySafe,
language::Language,
local_user::{LocalUser, LocalUserSettings},
person::{Person, PersonSafe, PersonSafeAlias1, PersonSafeAlias2},
post::Post,
@ -83,6 +84,7 @@ pub struct PostView {
pub read: bool, // Left join to PostRead
pub creator_blocked: bool, // Left join to PersonBlock
pub my_vote: Option<i16>, // Left join to PostLike
pub language: Language,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
@ -105,3 +107,9 @@ pub struct SiteView {
pub site: Site,
pub counts: SiteAggregates,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LocalUserDiscussionLanguageView {
pub local_user: LocalUserSettings,
pub language: Language,
}

View file

@ -8,7 +8,7 @@ use lemmy_api_common::{
community::CommunityResponse,
person::PrivateMessageResponse,
post::PostResponse,
utils::{blocking, check_person_block, get_user_lang, send_email_to_user},
utils::{blocking, check_person_block, get_interface_language, send_email_to_user},
};
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
@ -213,7 +213,7 @@ pub async fn send_local_notifs(
// Send an email to those local users that have notifications on
if do_send_email {
let lang = get_user_lang(&mention_user_view);
let lang = get_interface_language(&mention_user_view);
send_email_to_user(
&mention_user_view,
&lang.notification_mentioned_by_subject(&person.name),
@ -263,7 +263,7 @@ pub async fn send_local_notifs(
.ok();
if do_send_email {
let lang = get_user_lang(&parent_user_view);
let lang = get_interface_language(&parent_user_view);
send_email_to_user(
&parent_user_view,
&lang.notification_comment_reply_subject(&person.name),
@ -304,7 +304,7 @@ pub async fn send_local_notifs(
.ok();
if do_send_email {
let lang = get_user_lang(&parent_user_view);
let lang = get_interface_language(&parent_user_view);
send_email_to_user(
&parent_user_view,
&lang.notification_post_reply_subject(&person.name),

View file

@ -0,0 +1,6 @@
alter table post drop column language_id;
drop table local_user_language;
drop table language;
alter table local_user rename column interface_language to lang;

View file

@ -0,0 +1,200 @@
create table language (
id serial primary key,
code varchar(3),
name text
);
create table local_user_language (
id serial primary key,
local_user_id int references local_user on update cascade on delete cascade not null,
language_id int references language on update cascade on delete cascade not null,
unique (local_user_id, language_id)
);
alter table local_user rename column lang to interface_language;
alter table post add column language_id integer references language not null default 0;
insert into language(id, code, name) values (0, 'und', 'Undetermined');
insert into language(code, name) values ('aa', 'Afaraf');
insert into language(code, name) values ('ab', 'аҧсуа бызшәа');
insert into language(code, name) values ('ae', 'avesta');
insert into language(code, name) values ('af', 'Afrikaans');
insert into language(code, name) values ('ak', 'Akan');
insert into language(code, name) values ('am', 'አማርኛ');
insert into language(code, name) values ('an', 'aragonés');
insert into language(code, name) values ('ar', 'اَلْعَرَبِيَّةُ');
insert into language(code, name) values ('as', 'অসমীয়া');
insert into language(code, name) values ('av', 'авар мацӀ');
insert into language(code, name) values ('ay', 'aymar aru');
insert into language(code, name) values ('az', 'azərbaycan dili');
insert into language(code, name) values ('ba', 'башҡорт теле');
insert into language(code, name) values ('be', 'беларуская мова');
insert into language(code, name) values ('bg', 'български език');
insert into language(code, name) values ('bi', 'Bislama');
insert into language(code, name) values ('bm', 'bamanankan');
insert into language(code, name) values ('bn', 'বাংলা');
insert into language(code, name) values ('bo', 'བོད་ཡིག');
insert into language(code, name) values ('br', 'brezhoneg');
insert into language(code, name) values ('bs', 'bosanski jezik');
insert into language(code, name) values ('ca', 'Català');
insert into language(code, name) values ('ce', 'нохчийн мотт');
insert into language(code, name) values ('ch', 'Chamoru');
insert into language(code, name) values ('co', 'corsu');
insert into language(code, name) values ('cr', 'ᓀᐦᐃᔭᐍᐏᐣ');
insert into language(code, name) values ('cs', 'čeština');
insert into language(code, name) values ('cu', 'ѩзыкъ словѣньскъ');
insert into language(code, name) values ('cv', 'чӑваш чӗлхи');
insert into language(code, name) values ('cy', 'Cymraeg');
insert into language(code, name) values ('da', 'dansk');
insert into language(code, name) values ('de', 'Deutsch');
insert into language(code, name) values ('dv', 'ދިވެހި');
insert into language(code, name) values ('dz', 'རྫོང་ཁ');
insert into language(code, name) values ('ee', 'Eʋegbe');
insert into language(code, name) values ('el', 'Ελληνικά');
insert into language(code, name) values ('en', 'English');
insert into language(code, name) values ('eo', 'Esperanto');
insert into language(code, name) values ('es', 'Español');
insert into language(code, name) values ('et', 'eesti');
insert into language(code, name) values ('eu', 'euskara');
insert into language(code, name) values ('fa', 'فارسی');
insert into language(code, name) values ('ff', 'Fulfulde');
insert into language(code, name) values ('fi', 'suomi');
insert into language(code, name) values ('fj', 'vosa Vakaviti');
insert into language(code, name) values ('fo', 'føroyskt');
insert into language(code, name) values ('fr', 'Français');
insert into language(code, name) values ('fy', 'Frysk');
insert into language(code, name) values ('ga', 'Gaeilge');
insert into language(code, name) values ('gd', 'Gàidhlig');
insert into language(code, name) values ('gl', 'galego');
insert into language(code, name) values ('gn', E'Avañe\'');
insert into language(code, name) values ('gu', 'ગુજરાતી');
insert into language(code, name) values ('gv', 'Gaelg');
insert into language(code, name) values ('ha', 'هَوُسَ');
insert into language(code, name) values ('he', 'עברית');
insert into language(code, name) values ('hi', 'हिन्दी');
insert into language(code, name) values ('ho', 'Hiri Motu');
insert into language(code, name) values ('hr', 'Hrvatski');
insert into language(code, name) values ('ht', 'Kreyòl ayisyen');
insert into language(code, name) values ('hu', 'magyar');
insert into language(code, name) values ('hy', 'Հայերեն');
insert into language(code, name) values ('hz', 'Otjiherero');
insert into language(code, name) values ('ia', 'Interlingua');
insert into language(code, name) values ('id', 'Bahasa Indonesia');
insert into language(code, name) values ('ie', 'Interlingue');
insert into language(code, name) values ('ig', 'Asụsụ Igbo');
insert into language(code, name) values ('ii', 'ꆈꌠ꒿ Nuosuhxop');
insert into language(code, name) values ('ik', 'Iñupiaq');
insert into language(code, name) values ('io', 'Ido');
insert into language(code, name) values ('is', 'Íslenska');
insert into language(code, name) values ('it', 'Italiano');
insert into language(code, name) values ('iu', 'ᐃᓄᒃᑎᑐᑦ');
insert into language(code, name) values ('ja', '日本語');
insert into language(code, name) values ('jv', 'basa Jawa');
insert into language(code, name) values ('ka', 'ქართული');
insert into language(code, name) values ('kg', 'Kikongo');
insert into language(code, name) values ('ki', 'Gĩkũyũ');
insert into language(code, name) values ('kj', 'Kuanyama');
insert into language(code, name) values ('kk', 'қазақ тілі');
insert into language(code, name) values ('kl', 'kalaallisut');
insert into language(code, name) values ('km', 'ខេមរភាសា');
insert into language(code, name) values ('kn', 'ಕನ್ನಡ');
insert into language(code, name) values ('ko', '한국어');
insert into language(code, name) values ('kr', 'Kanuri');
insert into language(code, name) values ('ks', 'कश्मीरी');
insert into language(code, name) values ('ku', 'Kurdî');
insert into language(code, name) values ('kv', 'коми кыв');
insert into language(code, name) values ('kw', 'Kernewek');
insert into language(code, name) values ('ky', 'Кыргызча');
insert into language(code, name) values ('la', 'latine');
insert into language(code, name) values ('lb', 'Lëtzebuergesch');
insert into language(code, name) values ('lg', 'Luganda');
insert into language(code, name) values ('li', 'Limburgs');
insert into language(code, name) values ('ln', 'Lingála');
insert into language(code, name) values ('lo', 'ພາສາລາວ');
insert into language(code, name) values ('lt', 'lietuvių kalba');
insert into language(code, name) values ('lu', 'Kiluba');
insert into language(code, name) values ('lv', 'latviešu valoda');
insert into language(code, name) values ('mg', 'fiteny malagasy');
insert into language(code, name) values ('mh', 'Kajin M̧ajeļ');
insert into language(code, name) values ('mi', 'te reo Māori');
insert into language(code, name) values ('mk', 'македонски јазик');
insert into language(code, name) values ('ml', 'മലയാളം');
insert into language(code, name) values ('mn', 'Монгол хэл');
insert into language(code, name) values ('mr', 'मराठी');
insert into language(code, name) values ('ms', 'Bahasa Melayu');
insert into language(code, name) values ('mt', 'Malti');
insert into language(code, name) values ('my', 'ဗမာစာ');
insert into language(code, name) values ('na', 'Dorerin Naoero');
insert into language(code, name) values ('nb', 'Norsk bokmål');
insert into language(code, name) values ('nd', 'isiNdebele');
insert into language(code, name) values ('ne', 'नेपाली');
insert into language(code, name) values ('ng', 'Owambo');
insert into language(code, name) values ('nl', 'Nederlands');
insert into language(code, name) values ('nn', 'Norsk nynorsk');
insert into language(code, name) values ('no', 'Norsk');
insert into language(code, name) values ('nr', 'isiNdebele');
insert into language(code, name) values ('nv', 'Diné bizaad');
insert into language(code, name) values ('ny', 'chiCheŵa');
insert into language(code, name) values ('oc', 'occitan');
insert into language(code, name) values ('oj', 'ᐊᓂᔑᓈᐯᒧᐎᓐ');
insert into language(code, name) values ('om', 'Afaan Oromoo');
insert into language(code, name) values ('or', 'ଓଡ଼ିଆ');
insert into language(code, name) values ('os', 'ирон æвзаг');
insert into language(code, name) values ('pa', 'ਪੰਜਾਬੀ');
insert into language(code, name) values ('pi', 'पाऴि');
insert into language(code, name) values ('pl', 'Polski');
insert into language(code, name) values ('ps', 'پښتو');
insert into language(code, name) values ('pt', 'Português');
insert into language(code, name) values ('qu', 'Runa Simi');
insert into language(code, name) values ('rm', 'rumantsch grischun');
insert into language(code, name) values ('rn', 'Ikirundi');
insert into language(code, name) values ('ro', 'Română');
insert into language(code, name) values ('ru', 'Русский');
insert into language(code, name) values ('rw', 'Ikinyarwanda');
insert into language(code, name) values ('sa', 'संस्कृतम्');
insert into language(code, name) values ('sc', 'sardu');
insert into language(code, name) values ('sd', 'सिन्धी');
insert into language(code, name) values ('se', 'Davvisámegiella');
insert into language(code, name) values ('sg', 'yângâ tî sängö');
insert into language(code, name) values ('si', 'සිංහල');
insert into language(code, name) values ('sk', 'slovenčina');
insert into language(code, name) values ('sl', 'slovenščina');
insert into language(code, name) values ('sm', E'gagana fa\'a Samoa');
insert into language(code, name) values ('sn', 'chiShona');
insert into language(code, name) values ('so', 'Soomaaliga');
insert into language(code, name) values ('sq', 'Shqip');
insert into language(code, name) values ('sr', 'српски језик');
insert into language(code, name) values ('ss', 'SiSwati');
insert into language(code, name) values ('st', 'Sesotho');
insert into language(code, name) values ('su', 'Basa Sunda');
insert into language(code, name) values ('sv', 'Svenska');
insert into language(code, name) values ('sw', 'Kiswahili');
insert into language(code, name) values ('ta', 'தமிழ்');
insert into language(code, name) values ('te', 'తెలుగు');
insert into language(code, name) values ('tg', 'тоҷикӣ');
insert into language(code, name) values ('th', 'ไทย');
insert into language(code, name) values ('ti', 'ትግርኛ');
insert into language(code, name) values ('tk', 'Türkmençe');
insert into language(code, name) values ('tl', 'Wikang Tagalog');
insert into language(code, name) values ('tn', 'Setswana');
insert into language(code, name) values ('to', 'faka Tonga');
insert into language(code, name) values ('tr', 'Türkçe');
insert into language(code, name) values ('ts', 'Xitsonga');
insert into language(code, name) values ('tt', 'татар теле');
insert into language(code, name) values ('tw', 'Twi');
insert into language(code, name) values ('ty', 'Reo Tahiti');
insert into language(code, name) values ('ug', 'ئۇيغۇرچە‎');
insert into language(code, name) values ('uk', 'Українська');
insert into language(code, name) values ('ur', 'اردو');
insert into language(code, name) values ('uz', 'Ўзбек');
insert into language(code, name) values ('ve', 'Tshivenḓa');
insert into language(code, name) values ('vi', 'Tiếng Việt');
insert into language(code, name) values ('vo', 'Volapük');
insert into language(code, name) values ('wa', 'walon');
insert into language(code, name) values ('wo', 'Wollof');
insert into language(code, name) values ('xh', 'isiXhosa');
insert into language(code, name) values ('yi', 'ייִדיש');
insert into language(code, name) values ('yo', 'Yorùbá');
insert into language(code, name) values ('za', 'Saɯ cueŋƅ');
insert into language(code, name) values ('zh', '中文');
insert into language(code, name) values ('zu', 'isiZulu');