228 lines
8.1 KiB
Haskell
228 lines
8.1 KiB
Haskell
-- SPDX-FileCopyrightText: 2022-2024 Felix Hamann <felix.hamann@campus.lmu.de>, Gregor Kleen <gregor@kleen.consulting>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de>, Steffen Jost <s.jost@fraport.de>
|
|
--
|
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
module Handler.Info where
|
|
|
|
import Import
|
|
import Handler.Utils
|
|
import Handler.Info.TH
|
|
|
|
import qualified Data.Map as Map
|
|
import Data.Map ((!))
|
|
import qualified Data.CaseInsensitive as CI
|
|
import qualified Data.Set as Set
|
|
|
|
-- import qualified Database.Esqueleto.Legacy as E
|
|
-- import qualified Database.Esqueleto.Utils as E
|
|
|
|
import Development.GitRev
|
|
|
|
-- import Auth.LDAP (ADError(..), ADInvalidCredentials(..), CampusMessage(..))
|
|
|
|
import Yesod.Auth.Message(AuthMessage(..))
|
|
|
|
|
|
pickLegalExternalLang :: Handler LegalExternal
|
|
pickLegalExternalLang = do
|
|
langMap <- $cachedHereBinary ("legal_external"::Text) makeMapLegalExternal
|
|
availLangs <- case nonEmpty' (Map.keys langMap) of
|
|
Just ls -> pure ls
|
|
Nothing -> $logErrorS "Legal" "Configuration of external legal links is missing." >> notFound
|
|
lang <- selectLanguage availLangs
|
|
return $ langMap ! lang
|
|
where
|
|
makeMapLegalExternal :: Handler (Map Lang LegalExternal)
|
|
makeMapLegalExternal = do
|
|
legExs <- getsYesod $ view _appLegalExternal
|
|
return $ Set.foldl' (\acc le -> Map.singleton (externalLanguage le) le <> acc) mempty legExs
|
|
-- return $ Map.fromAscList [(externalLanguage le,le) | le <- Set.toAscList legExs]
|
|
|
|
|
|
-- | Versionsgeschichte
|
|
getVersionR :: Handler TypedContent
|
|
getVersionR = selectRep $ do
|
|
provideRep $
|
|
return ($(orGitRevisionEnv gitDescribe) :: Text)
|
|
provideRep getInfoR
|
|
|
|
|
|
getImprintR :: Handler Html
|
|
getImprintR = do
|
|
le <- pickLegalExternalLang
|
|
redirect $ externalImprint le
|
|
|
|
getDataProtectionR :: Handler Html
|
|
getDataProtectionR = do
|
|
le <- pickLegalExternalLang
|
|
redirect $ externalDataProtection le
|
|
|
|
getPaymentsR :: Handler Html
|
|
getPaymentsR = do
|
|
le <- pickLegalExternalLang
|
|
redirect $ externalPayments le
|
|
|
|
getTermsOfUseR :: Handler Html
|
|
getTermsOfUseR = do
|
|
le <- pickLegalExternalLang
|
|
redirect $ externalTermsOfUse le
|
|
|
|
getInfoSupervisorR :: Handler Html
|
|
getInfoSupervisorR = error "TODO"
|
|
|
|
-- | Datenschutzerklaerung und Aufbewahrungspflichten, Nutzungsbedingungen, Urheberrecht, Impressum
|
|
getLegalR :: Handler Html
|
|
getLegalR = do
|
|
siteLayoutMsg MsgHeadingLegal $ do
|
|
setTitleI MsgLegalHeading
|
|
let dataProtection = $(i18nWidgetFile "data-protection")
|
|
termsUse = $(i18nWidgetFile "terms-of-use")
|
|
copyright = $(i18nWidgetFile "copyright")
|
|
imprint = $(i18nWidgetFile "imprint")
|
|
$(widgetFile "legal")
|
|
|
|
|
|
-- | Allgemeine Informationen
|
|
getInfoR :: Handler Html
|
|
getInfoR = do
|
|
changelogEntries' <- runDB $ selectList [ ChangelogItemFirstSeenItem <-. universeF ] []
|
|
let changelogEntries = Map.fromListWith Set.union
|
|
[ (Down changelogItemFirstSeenFirstSeen, Set.singleton changelogItemFirstSeenItem)
|
|
| Entity _ ChangelogItemFirstSeen{..} <- changelogEntries'
|
|
]
|
|
changelogItems = $(i18nWidgetFiles "changelog")
|
|
|
|
siteLayoutMsg MsgInfoHeading $ do
|
|
setTitleI MsgInfoHeading
|
|
let features = $(i18nWidgetFile "featureList")
|
|
changeLog = $(widgetFile "changelog")
|
|
knownBugs = $(i18nWidgetFile "knownBugs")
|
|
implementation = $(i18nWidgetFile "implementation")
|
|
gitInfo :: Text
|
|
gitInfo = $(orGitRevisionEnv gitDescribeDate)
|
|
$(widgetFile "versionHistory")
|
|
|
|
|
|
getGlossaryR :: Handler Html
|
|
getGlossaryR =
|
|
siteLayoutMsg MsgGlossaryTitle $ do
|
|
setTitleI MsgGlossaryTitle
|
|
MsgRenderer mr <- getMsgRenderer
|
|
let
|
|
entries' = sortOn (CI.mk . view _2) $ do
|
|
(k, v) <- Map.toList entries
|
|
msg <- maybeToList $ Map.lookup k msgMap
|
|
return (k, mr msg, v)
|
|
$(widgetFile "glossary")
|
|
where
|
|
entries = $(i18nWidgetFiles "glossary")
|
|
msgMap = $(glossaryTerms "glossary")
|
|
|
|
|
|
mkI18nWidgetEnum "FAQ" "faq"
|
|
mkMessageFor ''UniWorX ''FAQItem "messages/faq" "de-de-formal"
|
|
|
|
faqsWidget :: ( MonadAP m
|
|
, MonadThrow m
|
|
)
|
|
=> Maybe Natural -> Maybe (Route UniWorX) -> m (Maybe Widget, Bool)
|
|
faqsWidget mLimit route = do
|
|
faqs <- for route $ \route' -> filterM (showFAQ route') universeF
|
|
MsgRenderer mr <- getMsgRenderer
|
|
let
|
|
rItems' = sortOn (CI.mk . views _1 mr) $ do
|
|
(k, wgt) <- Map.toList items
|
|
msg <- maybeToList $ Map.lookup k faqItemMap
|
|
whenIsJust faqs $ \faqs' ->
|
|
guard $ msg `elem` faqs'
|
|
return (msg, wgt)
|
|
|
|
rItems <- case (,) <$> route <*> mLimit of
|
|
Nothing -> return rItems'
|
|
Just (route', limit) -> do
|
|
let wIndices = zip [0..] rItems'
|
|
wPrios <- forM wIndices $ \x@(_, (msg, _)) -> (, x) . Just <$> prioFAQ route' msg
|
|
let prioLimited = go Nothing [] $ sortOn (views _1 Down) wPrios
|
|
where
|
|
go _ acc [] = acc
|
|
go maxP acc ((p, x) : xs)
|
|
| maxP == Just p || length acc < fromIntegral limit
|
|
= go (Just p) (x : acc) xs
|
|
| otherwise
|
|
= acc
|
|
return . map (view _2) $ sortOn (view _1) prioLimited
|
|
|
|
let truncated = length rItems < length rItems'
|
|
|
|
return ( guardOn (not $ null rItems') $(widgetFile "faq")
|
|
, truncated
|
|
)
|
|
where
|
|
items = $(i18nWidgetFiles "faq")
|
|
|
|
faqLink :: FAQItem -> Widget
|
|
faqLink = toWidget <=< toTextUrl . (FaqR :#:)
|
|
|
|
|
|
getFaqR :: Handler Html
|
|
getFaqR =
|
|
siteLayoutMsg MsgFaqTitle $ do
|
|
setTitleI MsgFaqTitle
|
|
|
|
fromMaybe mempty . view _1 =<< faqsWidget Nothing Nothing
|
|
|
|
showFAQ :: ( MonadAP m
|
|
, MonadThrow m
|
|
)
|
|
=> Route UniWorX -> FAQItem -> m Bool
|
|
showFAQ _ FAQLoginExpired = return True
|
|
showFAQ _ FAQNoCampusAccount = is _Nothing <$> maybeAuthId
|
|
showFAQ (AuthR _) FAQCampusCantLogin = return True
|
|
showFAQ _ FAQCampusCantLogin = is _Nothing <$> maybeAuthId
|
|
showFAQ (AuthR _) FAQForgottenPassword = return True
|
|
showFAQ _ FAQForgottenPassword = is _Nothing <$> maybeAuthId
|
|
showFAQ _ FAQNotLecturerHowToCreateCourses
|
|
= and2M (is _Just <$> maybeAuthId)
|
|
(not <$> hasWriteAccessTo CourseNewR)
|
|
-- showFAQ (CourseR tid ssh csh _) FAQCourseCorrectorsTutors
|
|
-- = and2M (is _Just <$> maybeAuthId)
|
|
-- (or2M (hasWriteAccessTo $ CourseR tid ssh csh SheetNewR)
|
|
-- (hasWriteAccessTo $ CourseR tid ssh csh CTutorialNewR)
|
|
-- )
|
|
-- showFAQ _ _ = return False
|
|
|
|
prioFAQ :: Monad m
|
|
=> Route UniWorX -> FAQItem -> m Rational
|
|
prioFAQ _ FAQLoginExpired = return 2
|
|
prioFAQ _ FAQNoCampusAccount = return 1
|
|
prioFAQ _ FAQCampusCantLogin = return 1
|
|
prioFAQ _ FAQForgottenPassword = return 1
|
|
prioFAQ _ FAQNotLecturerHowToCreateCourses = return 1
|
|
|
|
|
|
getInfoLecturerR :: Handler Html
|
|
getInfoLecturerR =
|
|
siteLayoutMsg MsgInfoLecturerTitle $ do
|
|
setTitleI MsgInfoLecturerTitle
|
|
$(i18nWidgetFile "info-lecturer")
|
|
where
|
|
tooltipNew, tooltipProblem, tooltipPlanned, tooltipNewU2W :: WidgetFor UniWorX ()
|
|
tooltipNew = [whamlet| _{MsgLecturerInfoTooltipNew} |]
|
|
tooltipProblem = [whamlet| _{MsgLecturerInfoTooltipProblem} |]
|
|
tooltipPlanned = [whamlet| _{MsgLecturerInfoTooltipPlanned} |]
|
|
tooltipNewU2W = [whamlet| _{MsgLecturerInfoTooltipNewU2W} |]
|
|
newU2WFeat, probFeatInline, plannedFeat, plannedFeatInline :: WidgetFor UniWorX ()
|
|
newU2WFeat = [whamlet| ^{iconTooltip tooltipNewU2W (Just IconAnnounce) True} |] -- to be used inside text blocks
|
|
probFeatInline = [whamlet| ^{iconTooltip tooltipProblem (Just IconProblem) True} |] -- to be used inside text blocks
|
|
plannedFeat = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) False} |]
|
|
plannedFeatInline = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) True} |] -- to be used inside text blocks
|
|
|
|
-- new feature with given introduction date
|
|
newFeat :: Integer -> Int -> Int -> WidgetFor UniWorX ()
|
|
newFeat y m d = do
|
|
currentTime <- liftIO getCurrentTime
|
|
let expiryTime = UTCTime (addGregorianMonthsRollOver 1 $ fromGregorian y m d) 0
|
|
if currentTime > expiryTime
|
|
then mempty
|
|
else toWidget [whamlet| ^{iconTooltip tooltipNew (Just IconNew) False} |]
|