Skip to content

WIP: Got started on role invitations #128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ ghcid:
dev-deps:
stack install ghcid

reset-database: destroy-create-db migration fixtures
reset-database: create-db-user destroy-create-db migration fixtures

reset-data: truncate-tables fixtures

Expand Down
1 change: 1 addition & 0 deletions src/Application.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,
import Handler.Admin
import Handler.Abstract
import Handler.Auth
import Handler.Conference.Roles
import Handler.Home
import Helpers.Email

Expand Down
3 changes: 0 additions & 3 deletions src/Foundation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ instance Yesod App where

defaultLayout w = do
p <- widgetToPageContent w
msgs <- getMessages
let pt = pageTitle p
title = case renderHtml pt of
"" -> "Moot"
Expand All @@ -52,8 +51,6 @@ instance Yesod App where
<title>#{title}
^{pageHead p}
<body>
$forall (status, msg) <- msgs
<p class="message #{status}">#{msg}
^{pageBody p}
|]

Expand Down
17 changes: 11 additions & 6 deletions src/Handler/Admin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,29 @@ renderConferenceAbstractTypes conf@(Entity conferenceId _)

getConferenceAbstractTypesR :: ConferenceId -> Handler Html
getConferenceAbstractTypesR conferenceId = do
(_, _, _, conference) <-
(_, _, conference) <-
requireOwnerForConference conferenceId
abstractTypes <- runDB $ getAbstractTypes (entityKey conference)
(abstractTypeFormWidget, _) <- generateFormPost abstractTypeForm
renderConferenceAbstractTypes conference abstractTypes abstractTypeFormWidget

postConferenceAbstractTypesR :: ConferenceId -> Handler Html
postConferenceAbstractTypesR conferenceId = do
(_, _, _, conference) <-
(_, _, conference) <-
requireOwnerForConference conferenceId
((result, abstractTypeFormWidget), _) <- runFormPost abstractTypeForm
case result of
FormSuccess (AbstractTypeForm name duration) -> do
abstractTypes <- runDB $ do
void $
insertEntity $
AbstractType (entityKey conference) name (makeTalkDuration duration)
AbstractType (entityKey conference) name (makeTalkDuration duration)
getAbstractTypes (entityKey conference)
renderConferenceAbstractTypes conference abstractTypes abstractTypeFormWidget
_ -> error "bluhhh"
err -> do
$logErrorSH err
abstractTypes <- runDB $ getAbstractTypes (entityKey conference)
renderConferenceAbstractTypes conference abstractTypes abstractTypeFormWidget

renderConferencesCallout :: [Entity Conference] -> Text -> Widget
renderConferencesCallout [] _ = return ()
Expand Down Expand Up @@ -175,8 +178,6 @@ renderSubmitterConferences userId = do
|]

renderConferenceSubmission :: Entity Conference
-- -> Entity AbstractType
-- -> Entity Abstract
-> [( Entity AbstractType
, Entity Abstract
)]
Expand Down Expand Up @@ -243,6 +244,10 @@ getConferenceDashboardR confId = do
<h5>
<a href="@{ConferenceSurrogateAbstractR confId}">
Submit surrogate abstract on behalf of a speaker
<h5>
<a href="@{ConferenceRolesR confId}">
Manage conference roles and invite new users

<div .medium-6>
<div. .medium-3 .column>
<form method=POST
Expand Down
29 changes: 15 additions & 14 deletions src/Handler/Auth.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ requireVerifiedUser = do
|]
(Just _) -> return user

requireOwner :: Handler (Entity User, Entity Owner)
requireOwner :: Handler (Entity User, Entity Account)
requireOwner = do
user <- requireUser
maybeOwner <- runDB $ getOwnerForUser (entityKey user)
Expand All @@ -63,7 +63,7 @@ requireAccount :: Handler (Entity User, Entity Account)
requireAccount = do
maybeAccount <- requestAccount
case maybeAccount of
(user, Nothing) -> do
(_, Nothing) -> do
$logWarn "Current user is not associated with an account"
permissionDenied noAccountsForUserMsg
(user, Just account) -> return (user, account)
Expand All @@ -79,9 +79,9 @@ requestAccount = do

requireOwnerForEntity
:: DBVal a
=> EntityField a (Key Owner)
=> EntityField a (Key Account)
-> (SqlExpr (Entity a) -> SqlQuery b)
-> Handler (Entity User, Entity Owner, Entity a)
-> Handler (Entity User, Entity Account, Entity a)
requireOwnerForEntity f w = do
(user, owner) <- requireOwner
maybeRec <- runDB $ getRecByField' f w (entityKey owner)
Expand All @@ -94,10 +94,10 @@ requireOwnerForEntity f w = do

requireOwnerForConference
:: ConferenceId
-> Handler ( Entity User, Entity Owner, Entity Account, Entity Conference)
-> Handler (Entity User, Entity Account, Entity Conference)
requireOwnerForConference conferenceId = do
(user, owner) <- requireOwner
maybeConfAcc <- runDB $ getAccAndConf owner
(user, account) <- requireOwner
maybeConfAcc <- runDB $ getAccAndConf (accountOwner $ entityVal account)
case maybeConfAcc of
Nothing -> do
$logWarn "Current user is not associated with an account"
Expand All @@ -106,18 +106,19 @@ requireOwnerForConference conferenceId = do
$logWarn "Current user is not associated with any conferences"
permissionDenied noConferencesForUserMsg
Just (acc, Just conf) ->
return (user, owner, acc, conf)
return (user, acc, conf)
where
getAccAndConf :: Entity Owner -> DB (Maybe (Entity Account, Maybe (Entity Conference)))
getAccAndConf owner =
getAccAndConf :: UserId -> DB (Maybe (Entity Account, Maybe (Entity Conference)))
getAccAndConf userId =
selectFirst $
from $ \(account `LeftOuterJoin` mConference) -> do
on (just (account ^. AccountId) ==. mConference ?. ConferenceAccount)
where_ (account ^. AccountOwner ==. val (entityKey owner))
where_ (account ^. AccountOwner ==. val userId)
where_ (mConference ?. ConferenceId ==. (just (val conferenceId)))
return (account, mConference)

-- TODO: requireAdmin
requireAdmin :: Handler (Entity User, Either (Entity Owner) (Entity Admin))
requireAdmin :: Handler (Entity User, Either (Entity Account) (Entity Admin))
requireAdmin = undefined

requireAdminForConference
Expand All @@ -130,8 +131,8 @@ requireAdminForConference conferenceId = do
Nothing -> do
let errMsg = "Conference does not exist"
$logWarn errMsg >> permissionDenied errMsg
Just (conf, owner) -> do
unless (ownerUser (entityVal owner) == entityKey user) $ do
Just (conf, _, ownerUser) -> do
unless (entityKey ownerUser == entityKey user) $ do
isAdmin <- runDB (isUserConferenceAdmin (entityKey user))
unless isAdmin $ do
let errMsg = "Current User is not an admin of the conference"
Expand Down
112 changes: 112 additions & 0 deletions src/Handler/Conference/Roles.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
module Handler.Conference.Roles where

import Import

import Colonnade hiding (fromMaybe)
import qualified Data.Aeson as A
import qualified Data.Map as M
import Data.UUID.V4 (nextRandom)
import Yesod.Colonnade
import qualified Yesod.Paginator as Page

import Handler.Admin
import Handler.Auth
import Helpers.Forms
import qualified Helpers.PaginateUtil as PageUtil
import Helpers.Views

getConferenceRolesR :: ConferenceId -> Handler Html
getConferenceRolesR confId = do
(_, confEnt@(Entity _ conference)) <- requireAdminForConference confId
let confName = conferenceName conference
(admins, editors) <- runDB $ rolesForConference confId
baseLayout Nothing $ do
setTitle (fromString (unpack confName))
[whamlet|
<article .grid-container>
^{renderConferenceWidget confEnt}
<div .medium-6 .cell>
<h5>
<a href="@{ConferenceInviteRoleR confId}">
Invite someone to be an editor on this conference
<h3>Users with roles on this conference
<h5>Admins
<ul>
$forall (Entity _ user, _) <- admins
<li>#{tshow user}
<h5>Editors
<ul>
$forall (Entity _ user, _) <- editors
<li>#{tshow user}
|]

data RoleInviteForm =
RoleInviteForm {
role :: Role
, email :: Email
} deriving (Eq, Show)

roleInviteForm :: Form RoleInviteForm
roleInviteForm = do
let roleList :: [(Text, Role)]
roleList =
[ ("Editor", EditorRole)
]
renderDivs $
RoleInviteForm
<$> areq (selectFieldList roleList)
(named "role" (placeheld "Role:")) (Just EditorRole)
<*> areq emailField' (named "email"
(placeheld "Email: ")) Nothing

conferenceInviteRoleView :: Entity Conference -> Widget -> Handler Html
conferenceInviteRoleView confEnt@(Entity confId conference) roleInviteWidget = do
let confName = conferenceName conference
baseLayout Nothing $ do
setTitle (fromString (unpack confName))
[whamlet|
<article .grid-container>
^{renderConferenceWidget confEnt}
<div .medium-6>
<h3>Invite someone to perform a role for this conference
<div .medium-6>
<form method="POST" action="@{ConferenceInviteRoleR confId}">
^{roleInviteWidget}
<input .button type="submit" value="Invite">
|]

inviteEmailToConferenceRole :: ConferenceId
-> Email
-> Role
-> Handler (Entity RoleInvitation)
inviteEmailToConferenceRole confId email role = do
emailInvitation <- runDB $ createRoleInvitation confId email role
-- let uuid = editorInvitationUuid emailInvitation
-- $logInfoSH uuid
return $ emailInvitation

getConferenceInviteRoleR :: ConferenceId -> Handler Html
getConferenceInviteRoleR confId = do
(_, confEnt) <- requireAdminForConference confId
(roleInviteWidget, _) <- generateFormPost roleInviteForm
conferenceInviteRoleView confEnt roleInviteWidget

postConferenceInviteRoleR :: ConferenceId -> Handler Html
postConferenceInviteRoleR confId = do
(_, confEnt) <- requireAdminForConference confId
((result, roleInviteWidget), _) <- runFormPost roleInviteForm
case result of
FormSuccess (RoleInviteForm role email) -> do
case role of
EditorRole -> do
_ <- inviteEmailToConferenceRole confId email role
(freshRoleInviteWidget, _) <- generateFormPost roleInviteForm
setMessage $ toHtml $ "You successfully invited: " <> tshow (unEmail email)
conferenceInviteRoleView confEnt freshRoleInviteWidget
err -> do
$logErrorSH err
conferenceInviteRoleView confEnt roleInviteWidget

getConferenceAcceptRoleR :: ConferenceId -> UUID -> Handler Html
getConferenceAcceptRoleR confId uuid = do
undefined
1 change: 0 additions & 1 deletion src/Helpers/Forms.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module Helpers.Forms where

import Import.NoFoundation

import qualified Data.Text as T
import qualified Text.Email.Validate as TEV

named :: Text -> FieldSettings master -> FieldSettings master
Expand Down
3 changes: 1 addition & 2 deletions src/Helpers/PaginateUtil.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ where
import Yesod.Paginator.Prelude
import Yesod.Core
import Yesod.Paginator.Pages
import Yesod.Persist.Core


-- | Utility for better custom pagination allowing only one runDB.
Expand All @@ -25,4 +24,4 @@ getCurrentPage :: MonadHandler m => m PageNumber
getCurrentPage = fromMaybe 1 . go <$> lookupGetParam "p"
where
go :: Maybe Text -> Maybe PageNumber
go mp = readIntegral . unpack =<< mp
go mp = readIntegral . unpack =<< mp
3 changes: 3 additions & 0 deletions src/Helpers/Views.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ baseLayout :: Maybe (Entity User)
-> HandlerFor App Html
baseLayout _ content = do
nav <- renderNav
msgs <- getMessages
defaultLayout $ do
-- We need AJAX here and there.
addScriptRemote "https://code.jquery.com/jquery-3.3.1.min.js"
Expand All @@ -15,6 +16,8 @@ baseLayout _ content = do
addStylesheet (StaticR css_app_css)
[whamlet|
^{nav}
$forall (status, msg) <- msgs
<p class="message #{status}">#{msg}
<br>
^{content}
^{renderFooter}
Expand Down
22 changes: 14 additions & 8 deletions src/Model.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import qualified Settings as S

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User sql=users
email Email sqltype=varchar(100)
name Text sqltype=varchar(100)
email Email sqltype=varchar(320)
name Text sqltype=varchar(256)
createdAt UTCTime
verifiedAt UTCTime Maybe
UniqueUserEmail email
Expand All @@ -53,15 +53,10 @@ Reset sql=resets
deriving Eq Show

Account sql=accounts
owner OwnerId
owner UserId
UniqueAccountOwner owner
deriving Eq Show

Owner sql=owners
user UserId
UniqueOwnerUser user
deriving Eq Show

Admin sql=admins
user UserId
conference ConferenceId
Expand All @@ -74,6 +69,17 @@ Editor sql=editors
UniqueEditorUserConference user conference
deriving Eq Show

RoleInvitation sql=role_invitations
email Email sqltype=varchar(320)
conference ConferenceId
role Role sqltype=varchar(100)
uuid UUID
createdAt UTCTime
verifiedAt UTCTime Maybe
user UserId Maybe
UniqueRoleInvitation email conference
deriving Eq Show

Conference sql=conferences
account AccountId
name Text
Expand Down
Loading