Merge branch 'feat/1.1.0/report' into test

This commit is contained in:
LinkinStars 2023-05-16 17:48:27 +08:00
commit 0e932df27a
17 changed files with 299 additions and 306 deletions

10
go.mod
View File

@ -29,10 +29,10 @@ require (
github.com/ory/dockertest/v3 v3.9.1 github.com/ory/dockertest/v3 v3.9.1
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405 github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405
github.com/segmentfault/pacman v1.0.3 github.com/segmentfault/pacman v1.0.4
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0 github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221219081300-f734f4a16aa0 github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093245-f9384b820548
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05 github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05 github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
@ -129,10 +129,10 @@ require (
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect go.uber.org/zap v1.23.0 // indirect
golang.org/x/image v0.1.0 // indirect golang.org/x/image v0.1.0 // indirect
golang.org/x/mod v0.6.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.5.0 // indirect golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.2.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect

18
go.sum
View File

@ -627,12 +627,14 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/segmentfault/pacman v1.0.3 h1:/K8LJHQMiCaCIvC/e8GQITpYTEG6RH4KTLTZjPTghl4= github.com/segmentfault/pacman v1.0.3 h1:/K8LJHQMiCaCIvC/e8GQITpYTEG6RH4KTLTZjPTghl4=
github.com/segmentfault/pacman v1.0.3/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs= github.com/segmentfault/pacman v1.0.3/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs=
github.com/segmentfault/pacman v1.0.4 h1:6UIXuMHUeYMWe5toflV9SXZQizRny1RczjZJLj9kul0=
github.com/segmentfault/pacman v1.0.4/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs=
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0 h1:4x0qG7H2M3qH7Yo2BhGrVlji1iTmRAWgINY/JyENeHs= github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0 h1:4x0qG7H2M3qH7Yo2BhGrVlji1iTmRAWgINY/JyENeHs=
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs= github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs=
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 h1:BlqTgc3/MYKG6vMI2MI+6o+7P4Gy5PXlawu185wPXAk= github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 h1:BlqTgc3/MYKG6vMI2MI+6o+7P4Gy5PXlawu185wPXAk=
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05/go.mod h1:prPjFam7MyZ5b3S9dcDOt2tMPz6kf7C9c243s9zSwPY= github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05/go.mod h1:prPjFam7MyZ5b3S9dcDOt2tMPz6kf7C9c243s9zSwPY=
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221219081300-f734f4a16aa0 h1:zaAwBSpwUVrV2BBs1f1hfkv0rY/KdZLyKK8U9NKiurI= github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093245-f9384b820548 h1:R+FH23Qrdp5ECuHXmZy4BvoO/x7m2wZgNeiC46+jqCQ=
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221219081300-f734f4a16aa0/go.mod h1:7QcRmnV7OYq4hNOOCWXT5HXnN/u756JUsqIW0Bw8n9E= github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093245-f9384b820548/go.mod h1:7QcRmnV7OYq4hNOOCWXT5HXnN/u756JUsqIW0Bw8n9E=
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05 h1:jcGZU2juv0L3eFEkuZYV14ESLUlWfGMWnP0mjOfrSZc= github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05 h1:jcGZU2juv0L3eFEkuZYV14ESLUlWfGMWnP0mjOfrSZc=
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05/go.mod h1:L4GqtXLoR73obTYqUQIzfkm8NG8pvZafxFb6KZFSSHk= github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05/go.mod h1:L4GqtXLoR73obTYqUQIzfkm8NG8pvZafxFb6KZFSSHk=
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05 h1:91is1nKNbfTOl8CvMYiFgg4c5Vmol+5mVmMV/jDXD+A= github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05 h1:91is1nKNbfTOl8CvMYiFgg4c5Vmol+5mVmMV/jDXD+A=
@ -822,8 +824,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -893,6 +895,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -981,8 +984,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1050,8 +1054,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,4 +1,18 @@
# The following fields are used for back-end # The following fields are used for back-end
# backend.email.tpl.change_email.title.other = Confirm your new email address for {{.SiteName}} by clicking on the following link:\u003cbr\u003e\u003cbr\u003e\n\n\u003ca href='{{.ChangeEmailUrl}}' target='_blank'\u003e{{.ChangeEmailUrl}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\nIf you did not request this change, please ignore this email.\n
# backend.email.tpl.change_email.body.other = [{{.SiteName}}] Confirm your new email address
# backend.email.tpl.new_answer.title.other = \u003cstrong\u003e\u003ca href='{{.AnswerUrl}}'\u003e{{.QuestionTitle}}\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003e{{.DisplayName}}:\u003c/small\u003e\u003cbr\u003e\n\u003cblockquote\u003e{{.AnswerSummary}}\u003c/blockquote\u003e\u003cbr\u003e\n\u003ca href='{{.AnswerUrl}}'\u003eView it on {{.SiteName}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003eYou are receiving this because you authored the thread. \u003ca href='{{.UnsubscribeUrl}}'\u003eUnsubscribe\u003c/a\u003e\u003c/small\u003e
# backend.email.tpl.new_answer.body.other = [{{.SiteName}}] {{.DisplayName}} answered your question
# backend.email.tpl.new_comment.title.other = \u003cstrong\u003e\u003ca href='{{.CommentUrl}}'\u003e{{.QuestionTitle}}\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003e{{.DisplayName}}:\u003c/small\u003e\u003cbr\u003e\n\u003cblockquote\u003e{{.CommentSummary}}\u003c/blockquote\u003e\u003cbr\u003e\n\u003ca href='{{.CommentUrl}}'\u003eView it on {{.SiteName}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003eYou are receiving this because you authored the thread. \u003ca href='{{.UnsubscribeUrl}}'\u003eUnsubscribe\u003c/a\u003e\u003c/small\u003e
# backend.email.tpl.new_comment.body.other = [{{.SiteName}}] {{.DisplayName}} commented on your post
# backend.email.tpl.pass_reset.title.other = Somebody asked to reset your password on [{{.SiteName}}].\u003cbr\u003e\u003cbr\u003e\n\nIf it was not you, you can safely ignore this email.\u003cbr\u003e\u003cbr\u003e\n\nClick the following link to choose a new password:\u003cbr\u003e\n\u003ca href='{{.PassResetUrl}}' target='_blank'\u003e{{.PassResetUrl}}\u003c/a\u003e\n
# backend.email.tpl.pass_reset.body.other = [{{.SiteName }}] Password reset
# backend.email.tpl.register.title.other = Welcome to {{.SiteName}}\u003cbr\u003e\u003cbr\u003e\n\nClick the following link to confirm and activate your new account:\u003cbr\u003e\n\u003ca href='{{.RegisterUrl}}' target='_blank'\u003e{{.RegisterUrl}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n
# backend.email.tpl.register.body.other = [{{.SiteName}}] Confirm your new account
# backend.email.tpl.test.title.other = This is a test email.
# backend.email.tpl.test.body.other = [{{.SiteName}}] Test Email
backend: backend:
base: base:
@ -250,41 +264,74 @@ backend:
upload: upload:
unsupported_file_format: unsupported_file_format:
other: Unsupported file format. other: Unsupported file format.
report: reason:
spam: spam:
name: name:
other: spam other: spam
desc: desc:
other: This post is an advertisement, or vandalism. It is not useful or relevant other: This post is an advertisement, or vandalism. It is not useful or relevant to the current topic.
to the current topic. rude_or_abusive:
rude:
name: name:
other: rude or abusive other: rude or abusive
desc: desc:
other: A reasonable person would find this content inappropriate for respectful other: A reasonable person would find this content inappropriate for respectful
discourse. discourse.
duplicate: a_duplicate:
name: name:
other: a duplicate other: a duplicate
desc: desc:
other: This question has been asked before and already has an answer. other: This question has been asked before and already has an answer.
not_answer: placeholder:
other: Enter the existing question link
not_a_answer:
name: name:
other: not an answer other: not an answer
desc: desc:
other: This was posted as an answer, but it does not attempt to answer the other: This was posted as an answer, but it does not attempt to answer the
question. It should possibly be an edit, a comment, another question, question. It should possibly be an edit, a comment, another question,
or deleted altogether. or deleted altogether.
not_need: no_longer_needed:
name: name:
other: no longer needed other: no longer needed
desc: desc:
other: This comment is outdated, conversational or not relevant to this post. other: This comment is outdated, conversational or not relevant to this post.
other: something:
name: name:
other: something else other: something else
desc: desc:
other: This post requires staff attention for another reason not listed above. other: This post requires staff attention for another reason not listed above.
placeholder:
other: Let us know specifically what you are concerned about
community_specific:
name:
other: a community-specific reason
desc:
other: This question doesnt meet a community guideline.
not_clarity:
name:
other: needs details or clarity
desc:
other: This question currently includes multiple questions in one. It should focus on one problem only.
looks_ok:
name:
other: looks ok
desc:
other: This post is good as-is and not low quality.
needs_edit:
name:
other: needs edit, and I did it
desc:
other: Improve and correct problems with this post yourself.
needs_close:
name:
other: needs close
desc:
other: A closed question cant answer, but still can edit, vote and comment.
needs_delete:
name:
other: needs delete
desc:
other: All reputation gained and lost will be restored.
question: question:
close: close:
duplicate: duplicate:
@ -351,6 +398,37 @@ backend:
other: downvoted answer other: downvoted answer
up_voted_comment: up_voted_comment:
other: upvoted comment other: upvoted comment
email_tpl:
change_email:
title:
other: "[{{.SiteName}}] Confirm your new email address"
body:
other: "Confirm your new email address for {{.SiteName}} by clicking on the following link:<br><br>\n\n<a href='{{.ChangeEmailUrl}}' target='_blank'>{{.ChangeEmailUrl}}</a><br><br>\n\nIf you did not request this change, please ignore this email.\n"
new_answer:
title:
other: "[{{.SiteName}}] {{.DisplayName}} answered your question"
body:
other: "<strong><a href='{{.AnswerUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.AnswerSummary}}</blockquote><br>\n<a href='{{.AnswerUrl}}'>View it on {{.SiteName}}</a><br><br>\n\n<small>You are receiving this because you authored the thread. <a href='{{.UnsubscribeUrl}}'>Unsubscribe</a></small>"
new_comment:
title:
other: "[{{.SiteName}}] {{.DisplayName}} commented on your post"
body:
other: "<strong><a href='{{.CommentUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.CommentSummary}}</blockquote><br>\n<a href='{{.CommentUrl}}'>View it on {{.SiteName}}</a><br><br>\n\n<small>You are receiving this because you authored the thread. <a href='{{.UnsubscribeUrl}}'>Unsubscribe</a></small>"
pass_reset:
title:
other: "[{{.SiteName }}] Password reset"
body:
other: "Somebody asked to reset your password on [{{.SiteName}}].<br><br>\n\nIf it was not you, you can safely ignore this email.<br><br>\n\nClick the following link to choose a new password:<br>\n<a href='{{.PassResetUrl}}' target='_blank'>{{.PassResetUrl}}</a>\n"
register:
title:
other: "[{{.SiteName}}] Confirm your new account"
body:
other: "Welcome to {{.SiteName}}<br><br>\n\nClick the following link to confirm and activate your new account:<br>\n<a href='{{.RegisterUrl}}' target='_blank'>{{.RegisterUrl}}</a><br><br>\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n"
test:
title:
other: "[{{.SiteName}}] Test Email"
body:
other: "This is a test email."
# The following fields are used for interface presentation(Front-end) # The following fields are used for interface presentation(Front-end)
ui: ui:

View File

@ -243,37 +243,71 @@ backend:
upload: upload:
unsupported_file_format: unsupported_file_format:
other: 不支持的文件格式。 other: 不支持的文件格式。
report: reason:
spam: spam:
name: name:
other: 垃圾信息 other: 垃圾信息
desc: desc:
other: 这个帖子是一个广告,或是破坏性行为。它对当前的主题没有用处,也不相关。 other: 这个帖子是一个广告,或是破坏性行为。它对当前的主题没有用处,也不相关。
rude: rude_or_abusive:
name: name:
other: 粗鲁或辱骂的 other: 粗鲁或辱骂的
desc: desc:
other: 一个有理智的人都会认为这种内容不适合进行尊重性的讨论。 other: 一个有理智的人都会认为这种内容不适合进行尊重性的讨论。
duplicate: a_duplicate:
name: name:
other: 重复信息 other: 重复信息
desc: desc:
other: 此问题以前就有人问过,而且已经有了答案。 other: 此问题以前就有人问过,而且已经有了答案。
not_answer: placeholder:
other: 请输入重复的问题的网址
not_a_answer:
name: name:
other: 不是答案 other: 不是答案
desc: desc:
other: 此帖子是作为一个答案发布的,但它并没有试图回答这个问题。总之,它可能应该是个编辑,评论,另一个问题或者被删除。 other: 此帖子是作为一个答案发布的,但它并没有试图回答这个问题。总之,它可能应该是个编辑,评论,另一个问题或者被删除。
not_need: no_longer_needed:
name: name:
other: 不再需要 other: 不再需要
desc: desc:
other: 此评论已过时,对话或与此帖子无关。 other: 此评论已过时,对话或与此帖子无关。
other: something:
name: name:
other: 其他原因 other: 其他原因
desc: desc:
other: 此帖子需要工作人员关注,因为是上述所列以外的其他理由。 other: 此帖子需要工作人员关注,因为是上述所列以外的其他理由。
placeholder:
other: 让我们具体了解你所关注的内容
community_specific:
name:
other: 特定社区原因
desc:
other: 这个问题不符合社区准则。
not_clarity:
name:
other: 需要细节或澄清
desc:
other: 此问题目前包含多个问题。它应该只关注一个问题。
looks_ok:
name:
other: 看起来不错
desc:
other: 这篇文章很好,不是低质量的。
needs_edit:
name:
other: 需要编辑,我已经编辑了
desc:
other: 自己改善和纠正这篇文章中的问题。
needs_close:
name:
other: 需要关闭
desc:
other: 关闭的问题不能回答,但仍然可以编辑、投票和评论。
needs_delete:
name:
other: 需要删除
desc:
other: 所有获得和失去的声望都将被恢复。
question: question:
close: close:
duplicate: duplicate:
@ -339,6 +373,37 @@ backend:
other: 踩了答案 other: 踩了答案
up_voted_comment: up_voted_comment:
other: 赞了评论 other: 赞了评论
email_tpl:
change_email:
title:
other: "[{{.SiteName}}] 确认您的新电子邮件地址"
body:
other: "请点击以下链接确认您在 {{.SiteName}} 上的新电子邮件地址:<br><br>\n\n<a href='{{.ChangeEmailUrl}}' target='_blank'>{{.ChangeEmailUrl}}</a><br><br>\n\n如果您没有请求此更改请忽略此电子邮件。\n"
new_answer:
title:
other: "[{{.SiteName}}] {{.DisplayName}} 回答了您的问题"
body:
other: "<strong><a href='{{.AnswerUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.AnswerSummary}}</blockquote><br>\n<a href='{{.AnswerUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您是该讨论的作者。<a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
new_comment:
title:
other: "[{{.SiteName}}] {{.DisplayName}} 评论了您的帖子"
body:
other: "<strong><a href='{{.CommentUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.CommentSummary}}</blockquote><br>\n<a href='{{.CommentUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您是该讨论的作者。<a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
pass_reset:
title:
other: "[{{.SiteName }}] 重置密码"
body:
other: "有人要求在 [{{.SiteName}}] 上重置您的密码。<br><br>\n\n如果这不是您的操作请安心忽略此电子邮件。<br><br>\n\n请点击以下链接选择一个新密码<br>\n<a href='{{.PassResetUrl}}' target='_blank'>{{.PassResetUrl}}</a>\n"
register:
title:
other: "[{{.SiteName}}] 确认您的新账户"
body:
other: "欢迎加入 {{.SiteName}}<br><br>\n\n请点击以下链接确认并激活您的新账户<br>\n<a href='{{.RegisterUrl}}' target='_blank'>{{.RegisterUrl}}</a><br><br>\n\n如果上面的链接不能点击请将其复制并粘贴到您的浏览器地址栏中。\n"
test:
title:
other: "[{{.SiteName}}] 测试邮件"
body:
other: "这是一封测试邮件。"
#The following fields are used for interface presentation(Front-end) #The following fields are used for interface presentation(Front-end)
ui: ui:
how_to_format: how_to_format:

View File

@ -0,0 +1,21 @@
package constant
const (
EmailTplKeyChangeEmailTitle = "email_tpl.change_email.title"
EmailTplKeyChangeEmailBody = "email_tpl.change_email.body"
EmailTplKeyNewAnswerTitle = "email_tpl.new_answer.title"
EmailTplKeyNewAnswerBody = "email_tpl.new_answer.body"
EmailTplKeyNewCommentTitle = "email_tpl.new_comment.title"
EmailTplKeyNewCommentBody = "email_tpl.new_comment.body"
EmailTplKeyPassResetTitle = "email_tpl.pass_reset.title"
EmailTplKeyPassResetBody = "email_tpl.pass_reset.body"
EmailTplKeyRegisterTitle = "email_tpl.register.title"
EmailTplKeyRegisterBody = "email_tpl.register.body"
EmailTplKeyTestTitle = "email_tpl.test.title"
EmailTplKeyTestBody = "email_tpl.test.body"
)

View File

@ -1,34 +0,0 @@
package constant
const (
ReportSpamName = "report.spam.name"
ReportSpamDescription = "report.spam.description"
ReportRudeName = "report.rude.name"
ReportRudeDescription = "report.rude.description"
ReportDuplicateName = "report.duplicate.name"
ReportDuplicateDescription = "report.duplicate.description"
ReportOtherName = "report.other.name"
ReportOtherDescription = "report.other.description"
ReportNotAnswerName = "report.not_answer.name"
ReportNotAnswerDescription = "report.not_answer.description"
ReportNotNeedName = "report.not_need.name"
ReportNotNeedDescription = "report.not_need.description"
// question close
QuestionCloseDuplicateName = "question.close.duplicate.name"
QuestionCloseDuplicateDescription = "question.close.duplicate.description"
QuestionCloseGuidelineName = "question.close.guideline.name"
QuestionCloseGuidelineDescription = "question.close.guideline.description"
QuestionCloseMultipleName = "question.close.multiple.name"
QuestionCloseMultipleDescription = "question.close.multiple.description"
QuestionCloseOtherName = "question.close.other.name"
QuestionCloseOtherDescription = "question.close.other.description"
)
const (
// TODO put this in database
// TODO need reason controller to resolve
QuestionCloseJSON = `[{"name":"question.close.duplicate.name","description":"question.close.duplicate.description","source":"question","type":1,"have_content":false,"content_type":""},{"name":"question.close.guideline.name","description":"question.close.guideline.description","source":"question","type":2,"have_content":false,"content_type":""},{"name":"question.close.multiple.name","description":"question.close.multiple.description","source":"question","type":3,"have_content":true,"content_type":"text"},{"name":"question.close.other.name","description":"question.close.other.description","source":"question","type":4,"have_content":true,"content_type":"textarea"}]`
QuestionReportJSON = `[{"name":"report.spam.name","description":"report.spam.description","source":"question","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"question","type":2,"have_content":false,"content_type":""},{"name":"report.duplicate.name","description":"report.duplicate.description","source":"question","type":3,"have_content":true,"content_type":"text"},{"name":"report.other.name","description":"report.other.description","source":"question","type":4,"have_content":true,"content_type":"textarea"}]`
AnswerReportJSON = `[{"name":"report.spam.name","description":"report.spam.description","source":"answer","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"answer","type":2,"have_content":false,"content_type":""},{"name":"report.not_answer.name","description":"report.not_answer.description","source":"answer","type":3,"have_content":false,"content_type":""},{"name":"report.other.name","description":"report.other.description","source":"answer","type":4,"have_content":true,"content_type":"textarea"}]`
CommentReportJSON = `[{"name":"report.spam.name","description":"report.spam.description","source":"comment","type":1,"have_content":false,"content_type":""},{"name":"report.rude.name","description":"report.rude.description","source":"comment","type":2,"have_content":false,"content_type":""},{"name":"report.not_need.name","description":"report.not_need.description","source":"comment","type":3,"have_content":true,"content_type":"text"},{"name":"report.other.name","description":"report.other.description","source":"comment","type":4,"have_content":true,"content_type":"textarea"}]`
)

View File

@ -131,3 +131,15 @@ func Tr(lang i18n.Language, data string) string {
} }
return translation return translation
} }
// TrWithData translate key with template data, it will replace the template data {{ .PlaceHolder }} in the translation.
func TrWithData(lang i18n.Language, key string, templateData any) string {
if GlobalTrans == nil {
return key
}
translation := GlobalTrans.TrWithData(lang, key, templateData)
if translation == key {
return GlobalTrans.TrWithData(i18n.DefaultLanguage, key, templateData)
}
return translation
}

View File

@ -500,20 +500,6 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview}) handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
} }
// CloseMsgList close question msg list
// @Summary close question msg list
// @Description close question msg list
// @Tags Question
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/closemsglist [get]
func (qc *QuestionController) CloseMsgList(ctx *gin.Context) {
resp, err := qc.questionService.CloseMsgList(ctx, handler.GetLang(ctx))
handler.HandleResponse(ctx, err, resp)
}
// SearchByTitleLike add question title like // SearchByTitleLike add question title like
// @Summary add question title like // @Summary add question title like
// @Description add question title like // @Description add question title like

View File

@ -55,21 +55,3 @@ func (rc *ReportController) AddReport(ctx *gin.Context) {
err = rc.reportService.AddReport(ctx, req) err = rc.reportService.AddReport(ctx, req)
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, nil)
} }
// GetReportTypeList get report type list
// @Summary get report type list
// @Description get report type list
// @Tags Report
// @Produce json
// @Param source query string true "report source" Enums(question, answer, comment, user)
// @Success 200 {object} handler.RespBody{data=[]schema.GetReportTypeResp}
// @Router /answer/api/v1/report/type/list [get]
func (rc *ReportController) GetReportTypeList(ctx *gin.Context) {
req := &schema.GetReportListReq{}
if handler.BindAndCheck(ctx, req) {
return
}
resp, err := rc.reportService.GetReportTypeList(ctx, handler.GetLang(ctx), req)
handler.HandleResponse(ctx, err, resp)
}

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/config" "github.com/answerdev/answer/internal/service/config"
"github.com/answerdev/answer/internal/service/reason_common" "github.com/answerdev/answer/internal/service/reason_common"
@ -21,43 +22,37 @@ func NewReasonRepo(configRepo config.ConfigRepo) reason_common.ReasonRepo {
} }
} }
func (rr *reasonRepo) ListReasons(ctx context.Context, objectType, action string) (resp []schema.ReasonItem, err error) { func (rr *reasonRepo) ListReasons(ctx context.Context, objectType, action string) (resp []*schema.ReasonItem, err error) {
var ( lang := handler.GetLangByCtx(ctx)
reasonAction = fmt.Sprintf("%s.%s.reasons", objectType, action) reasonAction := fmt.Sprintf("%s.%s.reasons", objectType, action)
reasonKeys []string resp = make([]*schema.ReasonItem, 0)
cfgValue string
)
resp = []schema.ReasonItem{}
reasonKeys, err = rr.configRepo.GetArrayString(reasonAction) reasonKeys, err := rr.configRepo.GetArrayString(reasonAction)
if err != nil { if err != nil {
return return nil, err
} }
for _, reasonKey := range reasonKeys { for _, reasonKey := range reasonKeys {
var ( cfgValue, err := rr.configRepo.GetString(reasonKey)
reasonType int
reason = schema.ReasonItem{}
)
cfgValue, err = rr.configRepo.GetString(reasonKey)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
continue continue
} }
err = json.Unmarshal([]byte(cfgValue), &reason) reason := &schema.ReasonItem{}
err = json.Unmarshal([]byte(cfgValue), reason)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
continue continue
} }
reasonType, err = rr.configRepo.GetConfigType(reasonKey) reason.Translate(reasonKey, lang)
reason.ReasonType, err = rr.configRepo.GetConfigType(reasonKey)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
continue continue
} }
reason.ReasonType = reasonType
resp = append(resp, reason) resp = append(resp, reason)
} }
return return resp, nil
} }

View File

@ -1,5 +1,10 @@
package schema package schema
import (
"github.com/answerdev/answer/internal/base/translator"
"github.com/segmentfault/pacman/i18n"
)
type ReasonItem struct { type ReasonItem struct {
ReasonType int `json:"reason_type"` ReasonType int `json:"reason_type"`
Name string `json:"name"` Name string `json:"name"`
@ -14,3 +19,24 @@ type ReasonReq struct {
// Action // Action
Action string `validate:"required" form:"action" json:"action"` Action string `validate:"required" form:"action" json:"action"`
} }
func (r *ReasonItem) Translate(keyPrefix string, lang i18n.Language) {
trField := func(fieldName, fieldData string) string {
// If fieldData is empty, means no need to translate
if len(fieldData) == 0 {
return fieldData
}
key := keyPrefix + "." + fieldName
fieldTr := translator.Tr(lang, key)
if fieldTr != key {
// If i18n key exists, return i18n value
return fieldTr
}
// If i18n key not exists, return fieldData original value
return fieldData + "没翻译"
}
r.Name = trField("name", r.Name)
r.Description = trField("desc", r.Description)
r.Placeholder = trField("placeholder", r.Placeholder)
}

View File

@ -1,14 +1,15 @@
package export package export
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"mime" "mime"
"time" "time"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/config" "github.com/answerdev/answer/internal/service/config"
@ -162,11 +163,6 @@ func (es *EmailService) GetSiteGeneral(ctx context.Context) (resp schema.SiteGen
} }
func (es *EmailService) RegisterTemplate(ctx context.Context, registerUrl string) (title, body string, err error) { func (es *EmailService) RegisterTemplate(ctx context.Context, registerUrl string) (title, body string, err error) {
emailConfig, err := es.GetEmailConfig()
if err != nil {
return
}
siteInfo, err := es.GetSiteGeneral(ctx) siteInfo, err := es.GetSiteGeneral(ctx)
if err != nil { if err != nil {
return return
@ -176,123 +172,59 @@ func (es *EmailService) RegisterTemplate(ctx context.Context, registerUrl string
RegisterUrl: registerUrl, RegisterUrl: registerUrl,
} }
title, err = es.parseTemplateData(emailConfig.RegisterTitle, templateData) lang := handler.GetLangByCtx(ctx)
if err != nil { title = translator.TrWithData(lang, constant.EmailTplKeyRegisterTitle, templateData)
return "", "", fmt.Errorf("email template parse error: %s", err) body = translator.TrWithData(lang, constant.EmailTplKeyRegisterBody, templateData)
}
body, err = es.parseTemplateData(emailConfig.RegisterBody, templateData)
if err != nil {
return "", "", fmt.Errorf("email template parse error: %s", err)
}
return title, body, nil return title, body, nil
} }
func (es *EmailService) PassResetTemplate(ctx context.Context, passResetUrl string) (title, body string, err error) { func (es *EmailService) PassResetTemplate(ctx context.Context, passResetUrl string) (title, body string, err error) {
ec, err := es.GetEmailConfig()
if err != nil {
return
}
siteinfo, err := es.GetSiteGeneral(ctx)
if err != nil {
return
}
templateData := PassResetTemplateData{SiteName: siteinfo.Name, PassResetUrl: passResetUrl}
tmpl, err := template.New("pass_reset_title").Parse(ec.PassResetTitle)
if err != nil {
return "", "", err
}
titleBuf := &bytes.Buffer{}
bodyBuf := &bytes.Buffer{}
err = tmpl.Execute(titleBuf, templateData)
if err != nil {
return "", "", err
}
tmpl, err = template.New("pass_reset_body").Parse(ec.PassResetBody)
if err != nil {
return "", "", err
}
err = tmpl.Execute(bodyBuf, templateData)
if err != nil {
return "", "", err
}
return titleBuf.String(), bodyBuf.String(), nil
}
func (es *EmailService) ChangeEmailTemplate(ctx context.Context, changeEmailUrl string) (title, body string, err error) {
ec, err := es.GetEmailConfig()
if err != nil {
return
}
siteinfo, err := es.GetSiteGeneral(ctx)
if err != nil {
return
}
templateData := ChangeEmailTemplateData{
SiteName: siteinfo.Name,
ChangeEmailUrl: changeEmailUrl,
}
tmpl, err := template.New("email_change_title").Parse(ec.ChangeTitle)
if err != nil {
return "", "", err
}
titleBuf := &bytes.Buffer{}
bodyBuf := &bytes.Buffer{}
err = tmpl.Execute(titleBuf, templateData)
if err != nil {
return "", "", err
}
tmpl, err = template.New("email_change_body").Parse(ec.ChangeBody)
if err != nil {
return "", "", err
}
err = tmpl.Execute(bodyBuf, templateData)
if err != nil {
return "", "", err
}
return titleBuf.String(), bodyBuf.String(), nil
}
// TestTemplate send test email template parse
func (es *EmailService) TestTemplate(ctx context.Context) (title, body string, err error) {
emailConfig, err := es.GetEmailConfig()
if err != nil {
return
}
siteInfo, err := es.GetSiteGeneral(ctx) siteInfo, err := es.GetSiteGeneral(ctx)
if err != nil { if err != nil {
return return
} }
templateData := TestTemplateData{
templateData := PassResetTemplateData{SiteName: siteInfo.Name, PassResetUrl: passResetUrl}
lang := handler.GetLangByCtx(ctx)
title = translator.TrWithData(lang, constant.EmailTplKeyPassResetTitle, templateData)
body = translator.TrWithData(lang, constant.EmailTplKeyPassResetBody, templateData)
return title, body, nil
}
func (es *EmailService) ChangeEmailTemplate(ctx context.Context, changeEmailUrl string) (title, body string, err error) {
siteInfo, err := es.GetSiteGeneral(ctx)
if err != nil {
return
}
templateData := ChangeEmailTemplateData{
SiteName: siteInfo.Name, SiteName: siteInfo.Name,
ChangeEmailUrl: changeEmailUrl,
} }
title, err = es.parseTemplateData(emailConfig.TestTitle, templateData) lang := handler.GetLangByCtx(ctx)
if err != nil { title = translator.TrWithData(lang, constant.EmailTplKeyChangeEmailTitle, templateData)
return "", "", fmt.Errorf("email template parse error: %s", err) body = translator.TrWithData(lang, constant.EmailTplKeyChangeEmailBody, templateData)
} return title, body, nil
}
body, err = es.parseTemplateData(emailConfig.TestBody, templateData) // TestTemplate send test email template parse
func (es *EmailService) TestTemplate(ctx context.Context) (title, body string, err error) {
siteInfo, err := es.GetSiteGeneral(ctx)
if err != nil { if err != nil {
return "", "", fmt.Errorf("email template parse error: %s", err) return
} }
templateData := TestTemplateData{SiteName: siteInfo.Name}
lang := handler.GetLangByCtx(ctx)
title = translator.TrWithData(lang, constant.EmailTplKeyTestTitle, templateData)
body = translator.TrWithData(lang, constant.EmailTplKeyTestBody, templateData)
return title, body, nil return title, body, nil
} }
// NewAnswerTemplate new answer template // NewAnswerTemplate new answer template
func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAnswerTemplateRawData) ( func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAnswerTemplateRawData) (
title, body string, err error) { title, body string, err error) {
emailConfig, err := es.GetEmailConfig()
if err != nil {
return
}
siteInfo, err := es.GetSiteGeneral(ctx) siteInfo, err := es.GetSiteGeneral(ctx)
if err != nil { if err != nil {
return return
@ -307,26 +239,15 @@ func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAn
} }
templateData.SiteName = siteInfo.Name templateData.SiteName = siteInfo.Name
title, err = es.parseTemplateData(emailConfig.NewAnswerTitle, templateData) lang := handler.GetLangByCtx(ctx)
if err != nil { title = translator.TrWithData(lang, constant.EmailTplKeyNewAnswerTitle, templateData)
return "", "", fmt.Errorf("email template parse error: %s", err) body = translator.TrWithData(lang, constant.EmailTplKeyNewAnswerBody, templateData)
}
body, err = es.parseTemplateData(emailConfig.NewAnswerBody, templateData)
if err != nil {
return "", "", fmt.Errorf("email template parse error: %s", err)
}
return title, body, nil return title, body, nil
} }
// NewCommentTemplate new comment template // NewCommentTemplate new comment template
func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewCommentTemplateRawData) ( func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewCommentTemplateRawData) (
title, body string, err error) { title, body string, err error) {
emailConfig, err := es.GetEmailConfig()
if err != nil {
return
}
siteInfo, err := es.GetSiteGeneral(ctx) siteInfo, err := es.GetSiteGeneral(ctx)
if err != nil { if err != nil {
return return
@ -347,31 +268,12 @@ func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewC
} }
templateData.SiteName = siteInfo.Name templateData.SiteName = siteInfo.Name
title, err = es.parseTemplateData(emailConfig.NewCommentTitle, templateData) lang := handler.GetLangByCtx(ctx)
if err != nil { title = translator.TrWithData(lang, constant.EmailTplKeyNewCommentTitle, templateData)
return "", "", fmt.Errorf("email template parse error: %s", err) body = translator.TrWithData(lang, constant.EmailTplKeyNewCommentBody, templateData)
}
body, err = es.parseTemplateData(emailConfig.NewCommentBody, templateData)
if err != nil {
return "", "", fmt.Errorf("email template parse error: %s", err)
}
return title, body, nil return title, body, nil
} }
func (es *EmailService) parseTemplateData(templateContent string, templateData interface{}) (parsedData string, err error) {
parsedDataBuf := &bytes.Buffer{}
tmpl, err := template.New("").Parse(templateContent)
if err != nil {
return "", err
}
err = tmpl.Execute(parsedDataBuf, templateData)
if err != nil {
return "", err
}
return parsedDataBuf.String(), nil
}
func (es *EmailService) GetEmailConfig() (ec *EmailConfig, err error) { func (es *EmailService) GetEmailConfig() (ec *EmailConfig, err error) {
emailConf, err := es.configRepo.GetString("email.config") emailConf, err := es.configRepo.GetString("email.config")
if err != nil { if err != nil {

View File

@ -30,7 +30,6 @@ import (
"github.com/answerdev/answer/pkg/uid" "github.com/answerdev/answer/pkg/uid"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/i18n"
"github.com/segmentfault/pacman/log" "github.com/segmentfault/pacman/log"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -132,22 +131,6 @@ func (qs *QuestionService) ReopenQuestion(ctx context.Context, req *schema.Reope
return nil return nil
} }
// CloseMsgList list close question condition
func (qs *QuestionService) CloseMsgList(ctx context.Context, lang i18n.Language) (
resp []*schema.GetCloseTypeResp, err error,
) {
resp = make([]*schema.GetCloseTypeResp, 0)
err = json.Unmarshal([]byte(constant.QuestionCloseJSON), &resp)
if err != nil {
return nil, errors.InternalServer(reason.UnknownError).WithError(err).WithStack()
}
for _, t := range resp {
t.Name = translator.Tr(lang, t.Name)
t.Description = translator.Tr(lang, t.Description)
}
return resp, err
}
func (qs *QuestionService) AddQuestionCheckTags(ctx context.Context, Tags []*entity.Tag) ([]string, error) { func (qs *QuestionService) AddQuestionCheckTags(ctx context.Context, Tags []*entity.Tag) ([]string, error) {
list := make([]string, 0) list := make([]string, 0)
for _, tag := range Tags { for _, tag := range Tags {

View File

@ -17,6 +17,6 @@ func NewReasonService(reasonRepo reason_common.ReasonRepo) *ReasonService {
} }
} }
func (rs ReasonService) GetReasons(ctx context.Context, req schema.ReasonReq) (resp []schema.ReasonItem, err error) { func (rs ReasonService) GetReasons(ctx context.Context, req schema.ReasonReq) (resp []*schema.ReasonItem, err error) {
return rs.reasonRepo.ListReasons(ctx, req.ObjectType, req.Action) return rs.reasonRepo.ListReasons(ctx, req.ObjectType, req.Action)
} }

View File

@ -7,5 +7,5 @@ import (
) )
type ReasonRepo interface { type ReasonRepo interface {
ListReasons(ctx context.Context, objectType, action string) (resp []schema.ReasonItem, err error) ListReasons(ctx context.Context, objectType, action string) (resp []*schema.ReasonItem, err error)
} }

View File

@ -1,18 +1,11 @@
package report package report
import ( import (
"encoding/json"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/object_info" "github.com/answerdev/answer/internal/service/object_info"
"github.com/answerdev/answer/internal/service/report_common" "github.com/answerdev/answer/internal/service/report_common"
"github.com/answerdev/answer/pkg/obj" "github.com/answerdev/answer/pkg/obj"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/i18n"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -56,26 +49,3 @@ func (rs *ReportService) AddReport(ctx context.Context, req *schema.AddReportReq
} }
return rs.reportRepo.AddReport(ctx, report) return rs.reportRepo.AddReport(ctx, report)
} }
// GetReportTypeList get report list all
func (rs *ReportService) GetReportTypeList(ctx context.Context, lang i18n.Language, req *schema.GetReportListReq) (
resp []*schema.GetReportTypeResp, err error,
) {
resp = make([]*schema.GetReportTypeResp, 0)
switch req.Source {
case constant.QuestionObjectType:
err = json.Unmarshal([]byte(constant.QuestionReportJSON), &resp)
case constant.AnswerObjectType:
err = json.Unmarshal([]byte(constant.AnswerReportJSON), &resp)
case constant.CommentObjectType:
err = json.Unmarshal([]byte(constant.CommentReportJSON), &resp)
}
if err != nil {
err = errors.BadRequest(reason.UnknownError)
}
for _, t := range resp {
t.Name = translator.Tr(lang, t.Name)
t.Description = translator.Tr(lang, t.Description)
}
return resp, err
}

View File

@ -3,7 +3,9 @@ package report_admin
import ( import (
"context" "context"
"github.com/answerdev/answer/internal/service/config" "github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/repo/config"
configrepo "github.com/answerdev/answer/internal/service/config"
"github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/htmltext"
"github.com/segmentfault/pacman/log" "github.com/segmentfault/pacman/log"
@ -31,7 +33,7 @@ type ReportAdminService struct {
questionRepo questioncommon.QuestionRepo questionRepo questioncommon.QuestionRepo
commentCommonRepo comment_common.CommentCommonRepo commentCommonRepo comment_common.CommentCommonRepo
reportHandle *report_handle_admin.ReportHandle reportHandle *report_handle_admin.ReportHandle
configRepo config.ConfigRepo configRepo configrepo.ConfigRepo
} }
// NewReportAdminService new report service // NewReportAdminService new report service
@ -43,7 +45,7 @@ func NewReportAdminService(
questionRepo questioncommon.QuestionRepo, questionRepo questioncommon.QuestionRepo,
commentCommonRepo comment_common.CommentCommonRepo, commentCommonRepo comment_common.CommentCommonRepo,
reportHandle *report_handle_admin.ReportHandle, reportHandle *report_handle_admin.ReportHandle,
configRepo config.ConfigRepo) *ReportAdminService { configRepo configrepo.ConfigRepo) *ReportAdminService {
return &ReportAdminService{ return &ReportAdminService{
reportRepo: reportRepo, reportRepo: reportRepo,
commonUser: commonUser, commonUser: commonUser,
@ -70,8 +72,6 @@ func (rs *ReportAdminService) ListReportPage(ctx context.Context, dto schema.Get
users map[string]*schema.UserBasicInfo users map[string]*schema.UserBasicInfo
) )
pageModel = &pager.PageModel{}
flags, total, err = rs.reportRepo.GetReportListPage(ctx, dto) flags, total, err = rs.reportRepo.GetReportListPage(ctx, dto)
if err != nil { if err != nil {
return return
@ -140,6 +140,7 @@ func (rs *ReportAdminService) HandleReported(ctx context.Context, req schema.Rep
} }
func (rs *ReportAdminService) parseObject(ctx context.Context, resp *[]*schema.GetReportListPageResp) { func (rs *ReportAdminService) parseObject(ctx context.Context, resp *[]*schema.GetReportListPageResp) {
lang := handler.GetLangByCtx(ctx)
var ( var (
res = *resp res = *resp
) )
@ -221,6 +222,7 @@ func (rs *ReportAdminService) parseObject(ctx context.Context, resp *[]*schema.G
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
r.Reason.Translate(config.ID2KeyMapping[r.ReportType], lang)
} }
if r.FlaggedType > 0 { if r.FlaggedType > 0 {
r.FlaggedReason = &schema.ReasonItem{ r.FlaggedReason = &schema.ReasonItem{
@ -230,6 +232,7 @@ func (rs *ReportAdminService) parseObject(ctx context.Context, resp *[]*schema.G
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
r.Reason.Translate(config.ID2KeyMapping[r.ReportType], lang)
} }
res[i] = r res[i] = r