summaryrefslogtreecommitdiff
path: root/owner
diff options
context:
space:
mode:
authorVitaly Minko <vitaly.minko@gmail.com>2018-10-23 19:33:36 +0300
committerVitaly Minko <vitaly.minko@gmail.com>2018-10-23 19:33:36 +0300
commit0f2b635160bc3a8fd29937144a46fab5b685972f (patch)
treeeae41129ba66c1dcf8650220a0cbcf0966a646e3 /owner
parent7cf13227aee08432b885051cd7fe2d9a81cf7db4 (diff)
Implemented third part of moderation feature: CLI.
Diffstat (limited to 'owner')
-rw-r--r--owner/owner.go5
-rw-r--r--owner/view.go141
2 files changed, 145 insertions, 1 deletions
diff --git a/owner/owner.go b/owner/owner.go
index 7bc2c16..2dd1acf 100644
--- a/owner/owner.go
+++ b/owner/owner.go
@@ -37,6 +37,7 @@ type Owner struct {
Subs subs.Subscriptions
Profile *Profile
Signer *crypto.Signer
+ View *View
storage *storage.Storage
}
@@ -150,11 +151,13 @@ func New(dir, nickname string, stor *storage.Storage) (*Owner, error) {
return nil, errors.Database
}
+ prf := NewProfile(db, u.ID())
return &Owner{
User: u,
Subs: sub,
- Profile: NewProfile(db, u.ID()),
+ Profile: prf,
Signer: crypto.NewSigner(privKey),
+ View: NewView(prf, stor),
storage: stor,
}, nil
}
diff --git a/owner/view.go b/owner/view.go
new file mode 100644
index 0000000..91da87d
--- /dev/null
+++ b/owner/view.go
@@ -0,0 +1,141 @@
+/*
+This file is part of Dscuss.
+Copyright (C) 2018 Vitaly Minko
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package owner
+
+import (
+ "vminko.org/dscuss/entity"
+ "vminko.org/dscuss/log"
+ "vminko.org/dscuss/storage"
+ "vminko.org/dscuss/thread"
+)
+
+// View personalizes content for the owner. It applies operation performed by
+// the owner's moderators.
+type View struct {
+ prf *Profile
+ stor *storage.Storage
+}
+
+func NewView(prf *Profile, stor *storage.Storage) *View {
+ return &View{prf, stor}
+}
+
+func (v *View) isUserBanned(uid *entity.ID) (bool, error) {
+ authOps, err := v.stor.GetOperationsOnUser(uid)
+ if err != nil {
+ log.Errorf("Failed to get operations on user %s: %v", uid.Shorten(), err)
+ return false, err
+ }
+ for _, o := range authOps {
+ isModer, err := v.prf.HasModerator(&o.AuthorID)
+ if err != nil {
+ log.Errorf("Failed to check whether %s is a moderator: %v",
+ &o.AuthorID, err)
+ return false, err
+ }
+ if isModer {
+ switch o.OperationType() {
+ case entity.OperationTypeBanUser:
+ return true, nil
+ default:
+ log.Fatal("BUG: unknown entity type %T.")
+ }
+ }
+ }
+ return false, nil
+}
+
+func (v *View) applyOperationToMessage(m *entity.Message, op *entity.Operation) *entity.Message {
+ switch op.OperationType() {
+ case entity.OperationTypeRemoveMessage:
+ return nil
+ default:
+ log.Fatal("BUG: unknown entity type %T.")
+ }
+ return m
+}
+
+func (v *View) ModerateMessage(m *entity.Message) (*entity.Message, error) {
+ isBanned, err := v.isUserBanned(&m.AuthorID)
+ if err != nil {
+ log.Errorf("Failed check whether %s is banned: %v", m.AuthorID.Shorten(), err)
+ return nil, err
+ }
+ if isBanned {
+ log.Debugf("Author of msg %s (user %s) is banned",
+ m.ID().Shorten(), m.AuthorID.Shorten())
+ return nil, nil
+ }
+ msgOps, err := v.stor.GetOperationsOnMessage(m.ID())
+ if err != nil {
+ log.Errorf("Failed to get operations on message %s: %v", m.ID().Shorten(), err)
+ return nil, err
+ }
+ for _, o := range msgOps {
+ isModer, err := v.prf.HasModerator(&o.AuthorID)
+ if err != nil {
+ log.Errorf("Failed to check whether %s is a moderator: %v",
+ &o.AuthorID, err)
+ return nil, err
+ }
+ if isModer {
+ m = v.applyOperationToMessage(m, o)
+ if m == nil {
+ break
+ }
+ }
+ }
+ return m, nil
+}
+
+func (v *View) ModerateMessages(brd []*entity.Message) (res []*entity.Message, err error) {
+ for _, m := range brd {
+ mm, err := v.ModerateMessage(m)
+ if err != nil {
+ log.Errorf("Error moderating message %s: %v", m.ID().Shorten(), err)
+ return nil, err
+ }
+ if mm != nil {
+ res = append(res, mm)
+ }
+ }
+ return res, nil
+}
+
+type ThreadModerator struct {
+ v *View
+}
+
+func (tm *ThreadModerator) Handle(n *thread.Node) (*entity.Message, error) {
+ m := n.Msg
+ if m == nil {
+ log.Fatal("Bug: thread node with nil message")
+ }
+ mm, err := tm.v.ModerateMessage(m)
+ if err != nil {
+ log.Errorf("Error moderating message %s: %v", m.ID().Shorten(), err)
+ return nil, err
+ }
+ return mm, nil
+}
+
+func (v *View) ModerateThread(t *thread.Node) (*thread.Node, error) {
+ tm := ThreadModerator{v}
+ tvis := thread.NewModeratingVisitor(&tm)
+ return t.Moderate(tvis)
+}