Bugfix: delete user cd /home/jost/programming/Haskell/Yesod/uniworx/templates

This commit is contained in:
SJost 2018-10-17 18:07:04 +02:00
parent 5a349f9b85
commit adde4ccdf6
13 changed files with 187 additions and 24 deletions

View File

@ -15,6 +15,9 @@ mail-object-domain: "_env:MAILOBJECT_DOMAIN:localhost"
mail-verp: mail-verp:
separator: "+" separator: "+"
at-replacement: "=" at-replacement: "="
mail-support:
name: null
email: "uni2work@ifi.lmu.de"
job-workers: "_env:JOB_WORKERS:10" job-workers: "_env:JOB_WORKERS:10"
job-flush-interval: "_env:JOB_FLUSH:30" job-flush-interval: "_env:JOB_FLUSH:30"

View File

@ -340,6 +340,8 @@ MailSheetActiveIntro courseName@Text termDesc@Text sheetName@SheetName: Sie kön
MailSubjectSheetInactive csh@CourseShorthand sheetName@SheetName: #{sheetName} in #{csh} kann nur noch kurze Zeit abgegeben werden MailSubjectSheetInactive csh@CourseShorthand sheetName@SheetName: #{sheetName} in #{csh} kann nur noch kurze Zeit abgegeben werden
MailSheetInactiveIntro courseName@Text termDesc@Text sheetName@SheetName: Dia Abgabefirst für #{sheetName} im Kurs #{courseName} (#{termDesc}) endet in Kürze. MailSheetInactiveIntro courseName@Text termDesc@Text sheetName@SheetName: Dia Abgabefirst für #{sheetName} im Kurs #{courseName} (#{termDesc}) endet in Kürze.
MailSubjectSupport: Supportanfrage
SheetTypeBonus: Bonus SheetTypeBonus: Bonus
SheetTypeNormal: Normal SheetTypeNormal: Normal
SheetTypePass: Bestehen SheetTypePass: Bestehen
@ -382,3 +384,5 @@ SheetCreateExisting: Folgende Pseudonyme haben bereits abgegeben:
UserAccountDeleted name@Text: Konto für #{name} wurde gelöscht! UserAccountDeleted name@Text: Konto für #{name} wurde gelöscht!
Dummy: TODO Message not defined!

View File

@ -2,6 +2,7 @@
{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE IncoherentInstances #-} -- why is this needed? Instance for "display deadline" ought to be clear {-# LANGUAGE IncoherentInstances #-} -- why is this needed? Instance for "display deadline" ought to be clear
@ -16,7 +17,14 @@ import Handler.Utils
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Text as Text
import Data.Text.Encoding (decodeUtf8')
import Data.Time hiding (formatTime) import Data.Time hiding (formatTime)
import Data.Universe
import Data.Universe.Helpers
import Network.Wai (requestHeaderReferer)
-- import qualified Data.Text as T -- import qualified Data.Text as T
-- import Yesod.Form.Bootstrap3 -- import Yesod.Form.Bootstrap3
@ -27,6 +35,8 @@ import Data.Time hiding (formatTime)
-- import Yesod.Colonnade -- import Yesod.Colonnade
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
import Jobs
-- import Text.Shakespeare.Text -- import Text.Shakespeare.Text
import Development.GitRev import Development.GitRev
@ -117,10 +127,10 @@ homeUser uid = do
cTime <- liftIO getCurrentTime cTime <- liftIO getCurrentTime
let fTime = addUTCTime (offSheetDeadlines * nominalDay) cTime let fTime = addUTCTime (offSheetDeadlines * nominalDay) cTime
tableData :: -- E.InnerJoin (E.InnerJoin (E.SqlExpr (Entity CourseParticipant)) tableData :: E.LeftOuterJoin
-- (E.SqlExpr (Entity Course ))) (E.InnerJoin (E.InnerJoin (E.SqlExpr (Entity CourseParticipant)) (E.SqlExpr (Entity Course))) (E.SqlExpr (Entity Sheet)))
-- (E.SqlExpr (Entity Sheet )) (E.InnerJoin (E.SqlExpr (Maybe (Entity Submission))) (E.SqlExpr (Maybe (Entity SubmissionUser))))
_ -> E.SqlQuery ( E.SqlExpr (E.Value (Key Term)) -> E.SqlQuery ( E.SqlExpr (E.Value (Key Term))
, E.SqlExpr (E.Value SchoolId) , E.SqlExpr (E.Value SchoolId)
, E.SqlExpr (E.Value CourseShorthand) , E.SqlExpr (E.Value CourseShorthand)
, E.SqlExpr (E.Value SheetName) , E.SqlExpr (E.Value SheetName)
@ -226,20 +236,69 @@ getVersionR = selectRep $ do
return ($gitDescribe :: Text) return ($gitDescribe :: Text)
-- helpForm
data HelpIdentOptions = HIAnonymous | HIUser | HIEmail
deriving (Eq, Ord, Bounded, Enum, Show, Read)
$( return [] ) -- forces order of splices, error otherwise, see https://ghc.haskell.org/trac/ghc/ticket/9813
instance Universe HelpIdentOptions where universe = universeDef
instance Finite HelpIdentOptions
instance PathPiece HelpIdentOptions where
toPathPiece = $(nullaryToPathPiece ''HelpIdentOptions [Text.intercalate "-" . map Text.toLower . unsafeTail . splitCamel])
fromPathPiece = finiteFromPathPiece
instance RenderMessage UniWorX HelpIdentOptions where
renderMessage _ _ opt = tshow opt -- TODO
data HelpForm = HelpForm
{ hfReferer:: Maybe Text
, hfUserId :: Either (Maybe Email) UserId
, hfRequest:: Text
}
helpForm :: Maybe Text -> Maybe UserId -> AForm _ HelpForm
helpForm mReferer mUid = HelpForm
<$> maybe (pure Nothing) (fmap Just . aforced textField (fslI MsgDummy)) mReferer
<*> multiActionA (fslI MsgDummy) identActions (HIUser <$ mUid)
<*> (unTextarea <$> areq textareaField (fslI MsgDummy) Nothing)
<* submitButton
where
identActions :: Map _ (AForm _ (Either (Maybe Email) UserId))
identActions = Map.fromList . catMaybes $
[ ( HIUser,) . pure . Right <$> mUid
, Just (HIAnonymous, pure (Left Nothing))
, Just (HIEmail, Left . Just <$> apreq emailField (fslI MsgDummy) Nothing)
]
getHelpR :: Handler Html getHelpR :: Handler Html
getHelpR = do getHelpR = postHelpR
-- can we get the previous route?
--who <- maybeAuth
--now <- getCurrentTime
--where <- getCurrentRoute
-- WAI getReferer
-- TODO: form for free input
defaultLayout $ do
setTitle "Hilfe"
[whamlet|TODO|]
postHelpR :: Handler Html postHelpR :: Handler Html
postHelpR = getHelpR postHelpR = do
mUid <- maybeAuthId
mRefererBS <- requestHeaderReferer <$> waiRequest
let mReferer = maybeRight . decodeUtf8' =<< mRefererBS
((res,formWidget),formEnctype) <- runFormPost $ renderAForm FormStandard $ helpForm mReferer mUid
case res of
FormSuccess (HelpForm{..}) -> do
now <- liftIO getCurrentTime
queueJob' $ JobHelpRequest { jSender = hfUserId
, jHelpRequest = hfRequest
, jRequestTime = now
, jReferer = hfReferer }
redirect $ HelpR
{-selectRep $ do
provideJson ()
provideRep (redirect $ HelpR :: Handler Html) -}
FormMissing -> return ()
FormFailure errs -> mapM_ (addMessage Error . toHtml) errs
defaultLayout $ do
setTitle "Hilfe" -- TODO: International
$(widgetFile "help")

View File

@ -193,7 +193,7 @@ deleteUser duid = do
forM_ singleSubmissions $ \(E.Value submissionId) -> do forM_ singleSubmissions $ \(E.Value submissionId) -> do
deleteFileIds <- map E.unValue <$> getSubmissionFiles submissionId deleteFileIds <- map E.unValue <$> getSubmissionFiles submissionId
deleteCascade submissionId deleteCascade submissionId
deleteWhere [FileId <-. deleteFileIds] -- TODO: throws exception for de-duplicated files deleteCascadeWhere [FileId <-. deleteFileIds] -- TODO: throws exception for de-duplicated files
deletedSubmissionGroups <- deleteSingleSubmissionGroups deletedSubmissionGroups <- deleteSingleSubmissionGroups
return ((length singleSubmissions, length groupSubmissions),deletedSubmissionGroups) return ((length singleSubmissions, length groupSubmissions),deletedSubmissionGroups)
@ -213,6 +213,7 @@ deleteUser duid = do
getSubmissionFiles subId = E.select $ E.from $ \file -> do getSubmissionFiles subId = E.select $ E.from $ \file -> do
E.where_ $ E.exists $ E.from $ \submissionFile -> do E.where_ $ E.exists $ E.from $ \submissionFile -> do
E.where_ $ submissionFile E.^. SubmissionFileSubmission E.==. E.val subId E.where_ $ submissionFile E.^. SubmissionFileSubmission E.==. E.val subId
E.&&. submissionFile E.^. SubmissionFileFile E.==. file E.^. FileId
return $ file E.^. FileId return $ file E.^. FileId
deleteSingleSubmissionGroups = E.deleteCount $ E.from $ \submissionGroup -> do deleteSingleSubmissionGroups = E.deleteCount $ E.from $ \submissionGroup -> do

View File

@ -566,3 +566,30 @@ multiAction acts defAction = do
accWidget act (Just w) = cons $(widgetFile "widgets/multiAction") accWidget act (Just w) = cons $(widgetFile "widgets/multiAction")
actionResults = Map.map fst results actionResults = Map.map fst results
return ((actionResults Map.!) =<< actionRes, $(widgetFile "widgets/multiActionCollect")) return ((actionResults Map.!) =<< actionRes, $(widgetFile "widgets/multiActionCollect"))
multiActionA :: (RenderMessage UniWorX action, PathPiece action, Ord action, Eq action)
=> FieldSettings UniWorX
-> Map action (AForm (HandlerT UniWorX IO) a)
-> Maybe action
-> AForm (HandlerT UniWorX IO) a
multiActionA FieldSettings{..} acts defAction = formToAForm $ do
(res, selView) <- multiAction acts defAction
fvId <- maybe newIdent return fsId
MsgRenderer mr <- getMsgRenderer
return (res,
[ FieldView
{ fvLabel = toHtml $ mr fsLabel
, fvTooltip = toHtml . mr <$> fsTooltip
, fvId
, fvInput = selView
, fvErrors = case res of
FormFailure [e] -> Just $ toHtml e
_ -> Nothing
, fvRequired = True
}
])

View File

@ -40,6 +40,7 @@ import Database.Persist.Sql (executeQQ, fromSqlKey, transactionSave)
import Data.Monoid (Last(..)) import Data.Monoid (Last(..))
import Data.Semigroup (Max(..)) import Data.Semigroup (Max(..))
import Data.Bitraversable
import Utils.Lens import Utils.Lens
import Utils.Sql import Utils.Sql
@ -515,3 +516,17 @@ performJob JobSendTestEmail{..} = mailT jMailContext $ do
* #{nD} * #{nD}
* #{nT} * #{nT}
|] :: TextUrl (Route UniWorX)) |] :: TextUrl (Route UniWorX))
performJob JobHelpRequest{..} = do
supportAddress <- getsYesod $ appMailSupport . appSettings
userInfo <- bitraverse return (runDB . getEntity) jSender
let userAddress = either (fmap $ Address Nothing)
(fmap $ \(Entity _ User{..}) -> Address (Just userDisplayName) (CI.original userEmail))
userInfo
mailT def $ do
_mailTo .= [supportAddress]
whenIsJust userAddress $ addMailHeader "Reply-To" . renderAddress
setSubjectI MsgMailSubjectSupport
setDate jRequestTime
rtime <- formatTimeMail SelFormatDateTime jRequestTime
addPart ($(ihamletFile "templates/mail/support.hamlet") :: HtmlUrlI18n UniWorXMessage (Route UniWorX))

View File

@ -19,8 +19,11 @@ import Data.List.NonEmpty (NonEmpty)
data Job = JobSendNotification { jRecipient :: UserId, jNotification :: Notification } data Job = JobSendNotification { jRecipient :: UserId, jNotification :: Notification }
| JobSendTestEmail { jEmail :: Text, jMailContext :: MailContext } | JobSendTestEmail { jEmail :: Email, jMailContext :: MailContext }
| JobQueueNotification { jNotification :: Notification } | JobQueueNotification { jNotification :: Notification }
| JobHelpRequest { jSender :: Either (Maybe Email) UserId
, jRequestTime :: UTCTime
, jHelpRequest :: Text, jReferer :: Maybe Text }
deriving (Eq, Ord, Show, Read, Generic, Typeable) deriving (Eq, Ord, Show, Read, Generic, Typeable)
data Notification = NotificationSubmissionRated { nSubmission :: SubmissionId } data Notification = NotificationSubmissionRated { nSubmission :: SubmissionId }
| NotificationSheetActive { nSheet :: SheetId } | NotificationSheetActive { nSheet :: SheetId }

View File

@ -42,7 +42,7 @@ module Mail
, replaceMailHeader, addMailHeader, removeMailHeader , replaceMailHeader, addMailHeader, removeMailHeader
, replaceMailHeaderI, addMailHeaderI , replaceMailHeaderI, addMailHeaderI
, setSubjectI, setMailObjectId, setMailObjectId' , setSubjectI, setMailObjectId, setMailObjectId'
, setDateCurrent , setDate, setDateCurrent
, setMailSmtpData , setMailSmtpData
, _mailFrom, _mailTo, _mailCc, _mailBcc, _mailHeaders, _mailParts , _mailFrom, _mailTo, _mailCc, _mailBcc, _mailHeaders, _mailParts
, _partType, _partEncoding, _partFilename, _partHeaders, _partContent , _partType, _partEncoding, _partFilename, _partHeaders, _partContent
@ -163,6 +163,10 @@ deriveJSON defaultOptions
} ''MailContext } ''MailContext
instance Hashable MailContext instance Hashable MailContext
instance Default MailContext where
def = MailContext { mcLanguages = def
, mcDateTimeFormat = def
}
makeLenses_ ''MailContext makeLenses_ ''MailContext
@ -450,10 +454,12 @@ setMailObjectId' oid = setMailObjectUUID . ciphertext =<< encrypt oid
setDateCurrent :: (MonadHandler m, YesodMail (HandlerSite m)) => MailT m () setDateCurrent :: (MonadHandler m, YesodMail (HandlerSite m)) => MailT m ()
setDateCurrent = do setDateCurrent = setDate =<< liftIO getCurrentTime
now <- liftIO getCurrentTime
setDate :: (MonadHandler m, YesodMail (HandlerSite m)) => UTCTime -> MailT m ()
setDate time = do
tz <- mailDateTZ tz <- mailDateTZ
let timeStr = formatTime defaultTimeLocale "%a, %d %b %Y %T %z" $ ZonedTime (utcToLocalTimeTZ tz now) (timeZoneForUTCTime tz now) let timeStr = formatTime defaultTimeLocale "%a, %d %b %Y %T %z" $ ZonedTime (utcToLocalTimeTZ tz time) (timeZoneForUTCTime tz time)
replaceMailHeader "Date" . Just $ pack timeStr replaceMailHeader "Date" . Just $ pack timeStr

View File

@ -124,8 +124,9 @@ fromPoints = round
instance DisplayAble Points instance DisplayAble Points
data SheetType data SheetType
= Bonus { maxPoints :: Points } = Bonus { maxPoints :: Points } -- Erhöht nicht das Maximum, wird gutgeschrieben
| Normal { maxPoints :: Points } | Normal { maxPoints :: Points } -- Erhöht das Maximum, wird gutgeschrieben
-- | Informational { maxPoints :: Points } -- Erhöht nicht das Maximum Keine Gutschrift
| Pass { maxPoints, passingPoints :: Points } | Pass { maxPoints, passingPoints :: Points }
| NotGraded | NotGraded
deriving (Show, Read, Eq) deriving (Show, Read, Eq)

View File

@ -82,6 +82,7 @@ data AppSettings = AppSettings
, appMailFrom :: Address , appMailFrom :: Address
, appMailObjectDomain :: Text , appMailObjectDomain :: Text
, appMailVerp :: VerpMode , appMailVerp :: VerpMode
, appMailSupport :: Address
, appJobWorkers :: Int , appJobWorkers :: Int
, appJobFlushInterval :: Maybe NominalDiffTime , appJobFlushInterval :: Maybe NominalDiffTime
, appJobCronInterval :: NominalDiffTime , appJobCronInterval :: NominalDiffTime

View File

@ -8,6 +8,7 @@
, DeriveGeneric , DeriveGeneric
, GeneralizedNewtypeDeriving , GeneralizedNewtypeDeriving
, OverloadedStrings , OverloadedStrings
, FlexibleInstances
#-} #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
@ -27,6 +28,7 @@ import System.Locale.Read
import Data.Time (TimeZone(..), TimeLocale(..)) import Data.Time (TimeZone(..), TimeLocale(..))
import Data.Time.Zones (TZ) import Data.Time.Zones (TZ)
import Data.Time.Zones.TH (includeSystemTZ) import Data.Time.Zones.TH (includeSystemTZ)
import Data.Time.Clock.POSIX
import Language.Haskell.TH import Language.Haskell.TH
import Language.Haskell.TH.Syntax (Lift(..)) import Language.Haskell.TH.Syntax (Lift(..))
@ -47,6 +49,8 @@ import Utils.PathPiece
deriving instance Lift TimeZone deriving instance Lift TimeZone
deriving instance Lift TimeLocale deriving instance Lift TimeLocale
instance Hashable UTCTime where
hashWithSalt s = hashWithSalt s . toRational . utcTimeToPOSIXSeconds
-- $(timeLocaleMap _) :: [Lang] -> TimeLocale -- $(timeLocaleMap _) :: [Lang] -> TimeLocale
timeLocaleMap :: [(Lang, String)] -- ^ Languages and matching locales, first is taken as default timeLocaleMap :: [(Lang, String)] -- ^ Languages and matching locales, first is taken as default
@ -105,3 +109,8 @@ instance ToJSONKey SelDateTimeFormat where
toJSONKey = toJSONKeyText $ \v -> let String txt = toJSON v in txt toJSONKey = toJSONKeyText $ \v -> let String txt = toJSON v in txt
instance FromJSONKey SelDateTimeFormat where instance FromJSONKey SelDateTimeFormat where
fromJSONKey = FromJSONKeyTextParser $ parseJSON . String fromJSONKey = FromJSONKeyTextParser $ parseJSON . String
instance {-# OVERLAPPING #-} Default (SelDateTimeFormat -> DateTimeFormat) where
def SelFormatDateTime = "%c"
def SelFormatDate = "%F"
def SelFormatTime = "%T"

5
templates/help.hamlet Normal file
View File

@ -0,0 +1,5 @@
Bitte beschreiben Sie Ihr Problem:
<form method=post action=@{HelpR} enctype=#{formEnctype}>
^{formWidget}

View File

@ -0,0 +1,29 @@
$newline never
\<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<body>
<dl>
$case userInfo
$of Left (Just email)
<dt> E-Mail
<dd> #{email}
$of Left Nothing
$of Right Nothing
<dt> Ungültige UserId erhalten!
$of Right (Just (Entity _ User{..}))
<dt> Name
<dd> #{userDisplayName}
<dt> E-Mail
<dd> #{userEmail}
$maybe matrnr <- userMatrikelnummer
<dt> Matrikelnummer
<dd> #{matrnr}
<dt> E-Mail Sprachen
$forall lang <- mailLanguages userMailLanguages
<dd> #{lang}
<dt> Zeit
<dd> #{rtime}
<p>
#{jHelpRequest}