Eventually-decentralized project hosting and management platform

[[ 🗃 ^WvWbo vervis ]] :: [📥 Inbox] [📤 Outbox] [🐤 Followers] [🤝 Collaborators] [🛠 Changes]

Clone

HTTPS: darcs clone https://vervis.peers.community/repos/WvWbo

SSH: darcs clone USERNAME@vervis.peers.community:WvWbo

Tags

TODO

src / Vervis / Handler /

Group.hs

{- This file is part of Vervis.
 -
 - Written in 2016, 2019, 2022, 2023 by fr33domlover <fr33domlover@riseup.net>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

module Vervis.Handler.Group
    ( getGroupR
    , getGroupInboxR
    , postGroupInboxR
    , getGroupOutboxR
    , getGroupOutboxItemR
    , getGroupFollowersR
    , getGroupMessageR

    , getGroupStampR








    {-
    , getGroupsR
    , postGroupsR
    , getGroupNewR
    , getGroupMembersR
    , postGroupMembersR
    , getGroupMemberNewR
    , getGroupMemberR
    , deleteGroupMemberR
    , postGroupMemberR
    -}
    )
where

import Control.Monad.Trans.Except
import Data.Text (Text)
import Data.Time.Clock
import Database.Persist
import Data.ByteString (ByteString)
import Yesod.Core
import Yesod.Core.Content (TypedContent)
import Yesod.Persist.Core

import Network.FedURI
import Yesod.ActivityPub
import Yesod.FedURI
import Yesod.Hashids
import Yesod.MonadSite

import qualified Web.ActivityPub as AP

import Vervis.Federation.Auth
import Vervis.FedURI
import Vervis.Foundation
import Vervis.Model
import Vervis.Recipient
import Vervis.Settings
import Vervis.Web.Actor

getGroupR :: KeyHashid Group -> Handler TypedContent
getGroupR groupHash = do
    groupID <- decodeKeyHashid404 groupHash
    (group, actor, sigKeyIDs) <- runDB $ do
        g <- get404 groupID
        let aid = groupActor g
        a <- getJust aid
        sigKeys <- selectKeysList [SigKeyActor ==. aid] [Asc SigKeyId]
        return (g, a, sigKeys)

    encodeRouteLocal <- getEncodeRouteLocal
    hashSigKey <- getEncodeKeyHashid
    perActor <- asksSite $ appPerActorKeys . appSettings

    let route mk = encodeRouteLocal $ mk groupHash
        groupAP = AP.Actor
            { AP.actorLocal = AP.ActorLocal
                { AP.actorId         = route GroupR
                , AP.actorInbox      = route GroupInboxR
                , AP.actorOutbox     = Just $ route GroupOutboxR
                , AP.actorFollowers  = Just $ route GroupFollowersR
                , AP.actorFollowing  = Nothing
                , AP.actorPublicKeys =
                    map (Left . encodeRouteLocal) $
                    if perActor
                        then map (GroupStampR groupHash . hashSigKey) sigKeyIDs
                        else [ActorKey1R, ActorKey2R]
                , AP.actorSshKeys    = []
                }
            , AP.actorDetail = AP.ActorDetail
                { AP.actorType       = AP.ActorTypeOther "Group"
                , AP.actorUsername   = Nothing
                , AP.actorName       = Just $ actorName actor
                , AP.actorSummary    = Just $ actorDesc actor
                }
            }

    provideHtmlAndAP groupAP $ redirectToPrettyJSON here
    where
    here = GroupR groupHash

getGroupInboxR :: KeyHashid Group -> Handler TypedContent
getGroupInboxR = getInbox GroupInboxR groupActor

postGroupInboxR :: KeyHashid Group -> Handler ()
postGroupInboxR groupHash = do
    groupID <- decodeKeyHashid404 groupHash
    postInbox $ LocalActorGroup groupID

getGroupOutboxR :: KeyHashid Group -> Handler TypedContent
getGroupOutboxR = getOutbox GroupOutboxR GroupOutboxItemR groupActor

getGroupOutboxItemR
    :: KeyHashid Group -> KeyHashid OutboxItem -> Handler TypedContent
getGroupOutboxItemR = getOutboxItem GroupOutboxItemR groupActor

getGroupFollowersR :: KeyHashid Group -> Handler TypedContent
getGroupFollowersR = getActorFollowersCollection GroupFollowersR groupActor

getGroupMessageR
    :: KeyHashid Group -> KeyHashid LocalMessage -> Handler TypedContent
getGroupMessageR _ _ = notFound

getGroupStampR :: KeyHashid Group -> KeyHashid SigKey -> Handler TypedContent
getGroupStampR = servePerActorKey groupActor LocalActorGroup





















{-
getGroupsR :: Handler Html
getGroupsR = do
    groups <- runDB $ select $ from $ \ (sharer, group) -> do
        where_ $ sharer ^. SharerId E.==. group ^. GroupIdent
        orderBy [asc $ sharer ^. SharerIdent]
        return sharer
    defaultLayout $(widgetFile "group/list")

postGroupsR :: Handler Html
postGroupsR = do
    ((result, widget), enctype) <- runFormPost newGroupForm
    case result of
        FormSuccess ng -> do
            now <- liftIO getCurrentTime
            pid <- requireAuthId
            runDB $ do
                let sharer = Sharer
                        { sharerIdent   = ngIdent ng
                        , sharerName    = ngName ng
                        , sharerCreated = now
                        }
                sid <- insert sharer
                let group = Group
                        { groupIdent = sid
                        }
                gid <- insert group
                let member = GroupMember
                        { groupMemberPerson = pid
                        , groupMemberGroup  = gid
                        , groupMemberRole   = GRAdmin
                        , groupMemberJoined = now
                        }
                insert_ member
            redirect $ SharerR $ ngIdent ng
        FormMissing -> do
            setMessage "Field(s) missing"
            defaultLayout $(widgetFile "group/new")
        FormFailure _l -> do
            setMessage "Group creation failed, see errors below"
            defaultLayout $(widgetFile "group/new")

getGroupNewR :: Handler Html
getGroupNewR = do
    ((_result, widget), enctype) <- runFormPost newGroupForm
    defaultLayout $(widgetFile "group/new")

getGroupMembersR :: ShrIdent -> Handler Html
getGroupMembersR shar = do
    (group, members) <- runDB $ do
        Entity sid s <- getBy404 $ UniqueSharer shar
        Entity gid _g <- getBy404 $ UniqueGroup sid
        ms <- select $ from $ \ (member, person, sharer) -> do
            where_ $
                member ^. GroupMemberGroup  E.==. val gid            &&.
                member ^. GroupMemberPerson E.==. person ^. PersonId &&.
                person ^. PersonIdent       E.==. sharer ^. SharerId
            orderBy
                [ asc $ member ^. GroupMemberRole
                , asc $ sharer ^. SharerIdent
                ]
            return sharer
        return (s, ms)
    defaultLayout $(widgetFile "group/member/list")

getgid :: ShrIdent -> AppDB GroupId
getgid shar = do
    Entity s _ <- getBy404 $ UniqueSharer shar
    Entity g _ <- getBy404 $ UniqueGroup s
    return g

postGroupMembersR :: ShrIdent -> Handler Html
postGroupMembersR shar = do
    ((result, widget), enctype) <-
        runFormPost $ newGroupMemberForm $ getgid shar
    case result of
        FormSuccess ngm -> do
            now <- liftIO getCurrentTime
            runDB $ do
                gid <- getgid shar
                pid <- do
                    Entity s _ <- getBy404 $ UniqueSharer $ ngmIdent ngm
                    Entity p _ <- getBy404 $ UniquePersonIdent s
                    return p
                let member = GroupMember
                        { groupMemberPerson = pid
                        , groupMemberGroup  = gid
                        , groupMemberRole   = ngmRole ngm
                        , groupMemberJoined = now
                        }
                insert_ member
            redirect $ GroupMemberR shar $ ngmIdent ngm
        FormMissing -> do
            setMessage "Field(s) missing"
            defaultLayout $(widgetFile "group/member/new")
        FormFailure _l -> do
            setMessage "Member insertion failed, see errors below"
            defaultLayout $(widgetFile "group/member/new")

getGroupMemberNewR :: ShrIdent -> Handler Html
getGroupMemberNewR shar = do
    ((_result, widget), enctype) <-
        runFormPost $ newGroupMemberForm $ getgid shar
    defaultLayout $(widgetFile "group/member/new")

getGroupMemberR :: ShrIdent -> ShrIdent -> Handler Html
getGroupMemberR grp memb = do
    member <- runDB $ do
        gid <- do
            Entity s _ <- getBy404 $ UniqueSharer grp
            Entity g _ <- getBy404 $ UniqueGroup s
            return g
        pid <- do
            Entity s _ <- getBy404 $ UniqueSharer memb
            Entity p _ <- getBy404 $ UniquePersonIdent s
            return p
        Entity _mid m <- getBy404 $ UniqueGroupMember pid gid
        return m
    defaultLayout $(widgetFile "group/member/one")

deleteGroupMemberR :: ShrIdent -> ShrIdent -> Handler Html
deleteGroupMemberR grp memb = do
    succ <- runDB $ do
        gid <- do
            Entity s _ <- getBy404 $ UniqueSharer grp
            Entity g _ <- getBy404 $ UniqueGroup s
            return g
        pid <- do
            Entity s _ <- getBy404 $ UniqueSharer memb
            Entity p _ <- getBy404 $ UniquePersonIdent s
            return p
        mm <-
            selectFirst
                [ GroupMemberGroup  ==. gid
                , GroupMemberPerson !=. pid
                , GroupMemberRole   ==. GRAdmin
                ]
                []
        case mm of
            Nothing -> return False
            Just _  -> do
                Entity mid _m <- getBy404 $ UniqueGroupMember pid gid
                delete mid
                return True
    setMessage $
        if succ
            then "Group member removed."
            else "Can’t leave a group without an admin."
    redirect $ GroupMembersR grp

postGroupMemberR :: ShrIdent -> ShrIdent -> Handler Html
postGroupMemberR grp memb = do
    mmethod <- lookupPostParam "_method"
    case mmethod of
        Just "DELETE" -> deleteGroupMemberR grp memb
        _             -> notFound
-}
[See repo JSON]