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 as E import qualified Database.Esqueleto.Utils as E import Development.GitRev import Auth.LDAP (ADError(..), ADInvalidCredentials(..)) -- | Versionsgeschichte getVersionR :: Handler TypedContent getVersionR = selectRep $ do provideRep $ return ($gitDescribe :: Text) provideRep getInfoR -- | Datenschutzerklaerung und Aufbewahrungspflichten, Nutzungsbedingungen, Urheberrecht, Impressum getLegalR :: Handler Html getLegalR = siteLayoutMsg MsgMenuLegal $ 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' ] siteLayoutMsg MsgInfoHeading $ do setTitleI MsgInfoHeading let features = $(i18nWidgetFile "featureList") changeLog = $(widgetFile "changelog") knownBugs = $(i18nWidgetFile "knownBugs") implementation = $(i18nWidgetFile "implementation") gitInfo :: Text gitInfo = $gitDescribe <> " (" <> $gitCommitDate <> ")" $(widgetFile "versionHistory") where changelogItems = $(i18nWidgetFiles "changelog") getInfoLecturerR :: Handler Html getInfoLecturerR = siteLayoutMsg MsgInfoLecturerTitle $ do setTitleI MsgInfoLecturerTitle $(i18nWidgetFile "info-lecturer") where allocationInfo = $(i18nWidgetFile "allocation-info") 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} |] 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 :: ( MonadHandler m, HandlerSite m ~ UniWorX , MonadThrow m , MonadUnliftIO 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 :: ( MonadHandler m, HandlerSite m ~ UniWorX , MonadThrow m , MonadUnliftIO m ) => Route UniWorX -> FAQItem -> m Bool 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 (CExamR tid ssh csh examn _) FAQExamPoints = and2M (hasWriteAccessTo $ CExamR tid ssh csh examn EEditR) noExamParts where noExamParts = liftHandler . runDB . E.selectNotExists . E.from $ \(examPart `E.InnerJoin` exam `E.InnerJoin` course) -> do E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse E.on $ exam E.^. ExamId E.==. examPart E.^. ExamPartExam E.where_ $ course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh E.&&. exam E.^. ExamName E.==. E.val examn showFAQ _ FAQInvalidCredentialsAdAccountDisabled = maybeT (return False) $ do guardM $ is _Nothing <$> maybeAuthId sessionError <- MaybeT $ lookupSessionJson SessionError guard $ sessionError == PermissionDenied (toPathPiece $ ADInvalidCredentials ADAccountDisabled) return True showFAQ _ FAQAllocationNoPlaces = maybeT (return False) $ do uid <- MaybeT maybeAuthId now <- liftIO getCurrentTime liftHandler . runDB . E.selectExists . E.from $ \allocation -> do let doneSince = E.subSelectMaybe . E.from $ \participant -> do E.where_ $ participant E.^. CourseParticipantAllocated E.==. E.just (allocation E.^. AllocationId) return . E.max_ $ participant E.^. CourseParticipantRegistration isAllocationUser = E.exists . E.from $ \allocationUser -> E.where_ $ allocationUser E.^. AllocationUserAllocation E.==. allocation E.^. AllocationId E.&&. allocationUser E.^. AllocationUserUser E.==. E.val uid isApplicant = E.exists . E.from $ \courseApplication -> E.where_ $ courseApplication E.^. CourseApplicationAllocation E.==. E.just (allocation E.^. AllocationId) E.&&. courseApplication E.^. CourseApplicationUser E.==. E.val uid E.where_ $ isAllocationUser E.||. isApplicant E.where_ $ E.maybe E.false (\done -> done E.>=. E.val (addUTCTime (-7 * nominalDay) now)) doneSince showFAQ _ _ = return False prioFAQ :: Monad m => Route UniWorX -> FAQItem -> m Rational prioFAQ _ FAQNoCampusAccount = return 1 prioFAQ _ FAQCampusCantLogin = return 1 prioFAQ _ FAQForgottenPassword = return 1 prioFAQ _ FAQNotLecturerHowToCreateCourses = return 1 prioFAQ _ FAQCourseCorrectorsTutors = return 1 prioFAQ _ FAQExamPoints = return 2 prioFAQ _ FAQAllocationNoPlaces = return 2 prioFAQ _ FAQInvalidCredentialsAdAccountDisabled = return 3