Merge branch 'master' into staging

This commit is contained in:
Felix Hamann 2018-07-08 23:54:34 +02:00
commit 69c2e95d83
11 changed files with 94 additions and 44 deletions

1
models
View File

@ -12,6 +12,7 @@ User json
UserAdmin UserAdmin
user UserId user UserId
school SchoolId school SchoolId
UniqueUserAdmin user school
UserLecturer UserLecturer
user UserId user UserId
school SchoolId school SchoolId

View File

@ -459,7 +459,6 @@ instance Yesod UniWorX where
defaultLayout widget = do defaultLayout widget = do
master <- getYesod master <- getYesod
mmsgs <- getMessages mmsgs <- getMessages
messageRender <- getMessageRender -- needed, since there is no i18n interpolation in Julius
mcurrentRoute <- getCurrentRoute mcurrentRoute <- getCurrentRoute
@ -493,10 +492,10 @@ instance Yesod UniWorX where
in (c, courseRoute, ) <$> filterM (menuItemAccessCallback . menuItem) (pageActions courseRoute) in (c, courseRoute, ) <$> filterM (menuItemAccessCallback . menuItem) (pageActions courseRoute)
let highlight :: Route UniWorX -> Bool -- highlight last route in breadcrumbs, favorites taking priority let highlight :: Route UniWorX -> Bool -- highlight last route in breadcrumbs, favorites taking priority
highlight = let crumbs = mcons mcurrentRoute $ fst <$> parents highlight = let crumbs = mcons mcurrentRoute $ fst <$> reverse parents
actFav = List.intersect (snd3 <$> favourites) crumbs navItems = map snd3 favourites ++ map (menuItemRoute . menuItem) menuTypes
highRs = if null actFav then crumbs else actFav highR = find (`elem` navItems) . uncurry (++) $ partition (`elem` map snd3 favourites) crumbs
in \r -> r `elem` highRs in \r -> Just r == highR
favouriteTerms :: [TermIdentifier] favouriteTerms :: [TermIdentifier]
favouriteTerms = Set.toDescList $ foldMap (\(Course{..}, _, _) -> Set.singleton $ unTermKey courseTerm) favourites favouriteTerms = Set.toDescList $ foldMap (\(Course{..}, _, _) -> Set.singleton $ unTermKey courseTerm) favourites
favouriteTerm :: TermIdentifier -> [(Course, Route UniWorX, [MenuTypes])] favouriteTerm :: TermIdentifier -> [(Course, Route UniWorX, [MenuTypes])]
@ -655,7 +654,7 @@ submissionList tid csh shn uid = E.select . E.from $ \(course `E.InnerJoin` shee
defaultLinks :: [MenuTypes] defaultLinks :: [MenuTypes]
defaultLinks = -- Define the menu items of the header. defaultLinks = -- Define the menu items of the header.
[ NavbarRight $ MenuItem [ NavbarAside $ MenuItem
{ menuItemLabel = "Home" { menuItemLabel = "Home"
, menuItemIcon = Just "home" , menuItemIcon = Just "home"
, menuItemRoute = HomeR , menuItemRoute = HomeR

View File

@ -13,6 +13,7 @@ import Import
import Handler.Utils import Handler.Utils
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
@ -41,7 +42,7 @@ getUsersR = do
E.orderBy [E.asc $ school E.^. SchoolShorthand] E.orderBy [E.asc $ school E.^. SchoolShorthand]
return $ school E.^. SchoolShorthand return $ school E.^. SchoolShorthand
return [whamlet| return [whamlet|
<ul> <ul .list--inline .list--comma-separated>
$forall (E.Value sh) <- schools $forall (E.Value sh) <- schools
<li>#{sh} <li>#{sh}
|] |]
@ -54,7 +55,7 @@ getUsersR = do
E.orderBy [E.asc $ school E.^. SchoolShorthand] E.orderBy [E.asc $ school E.^. SchoolShorthand]
return $ school E.^. SchoolShorthand return $ school E.^. SchoolShorthand
return [whamlet| return [whamlet|
<ul> <ul .list--inline .list--comma-separated>
$forall (E.Value sh) <- schools $forall (E.Value sh) <- schools
<li>#{sh} <li>#{sh}
|] |]
@ -95,7 +96,15 @@ postAdminHijackUserR cID = do
case hijackRes of case hijackRes of
FormSuccess uid' FormSuccess uid'
| uid' == uid -> do | uid' == uid -> do
User{..} <- runDB $ get404 uid myUid <- requireAuthId
User{..} <- runDB $ do
otherSchoolsAdmin <- Set.fromList . map (userAdminSchool . entityVal) <$> selectList [UserAdminUser ==. uid] []
otherSchoolsLecturer <- Set.fromList . map (userLecturerSchool . entityVal) <$> selectList [UserLecturerUser ==. uid] []
mySchools <- Set.fromList . map (userAdminSchool . entityVal) <$> selectList [UserAdminUser ==. myUid] []
when (not $ (otherSchoolsAdmin `Set.union` otherSchoolsLecturer) `Set.isSubsetOf` mySchools) $
permissionDenied "Cannot escalate admin status to additional schools"
get404 uid
setCredsRedirect $ Creds "dummy" (userPlugin <> ":" <> userIdent) [] setCredsRedirect $ Creds "dummy" (userPlugin <> ":" <> userIdent) []
| otherwise -> error "This should be impossible by definition of `hijackUserForm`" | otherwise -> error "This should be impossible by definition of `hijackUserForm`"
FormFailure errs -> toTypedContent <$> mapM_ (addMessage "error" . toHtml) errs FormFailure errs -> toTypedContent <$> mapM_ (addMessage "error" . toHtml) errs

View File

@ -8,10 +8,10 @@
Der Handler sollte jeweils aktuelle Beispiele für alle möglichen Funktionalitäten enthalten, so dass man immer weiß, wo man nachschlagen kann. Der Handler sollte jeweils aktuelle Beispiele für alle möglichen Funktionalitäten enthalten, so dass man immer weiß, wo man nachschlagen kann.
<div .container> <div .container.js-show-hide>
<h2 .js-show-hide__toggle>Teilweise funktionierende Abschnitte <h2 .js-show-hide__toggle>Teilweise funktionierende Abschnitte
<ul> <ul .js-show-hide__target>
<li .list-group-item> <li .list-group-item>
<a href=@{UsersR}>Benutzer Verwaltung <a href=@{UsersR}>Benutzer Verwaltung

View File

@ -115,10 +115,6 @@ ul {
list-style-type: none; list-style-type: none;
} }
.list--inline > li {
display: inline-block;
}
h1, h2, h3, h4, h5 { h1, h2, h3, h4, h5 {
font-weight: 600; font-weight: 600;
} }
@ -214,14 +210,14 @@ h4 {
@media (max-width: 768px) { @media (max-width: 768px) {
.main__content-body { .main__content-body {
padding: 0 20px 60px; padding: 30px 20px 60px;
} }
} }
@media (max-width: 425px) { @media (max-width: 425px) {
.main__content-body { .main__content-body {
padding: 0 10px 60px; padding: 20px 10px 60px;
} }
} }
@ -422,6 +418,22 @@ input[type="button"].btn-info:hover,
line-height: 25px; line-height: 25px;
} }
/* LIST MODIFIERS */
.list--inline > li {
display: inline-block;
}
.list--comma-separated > li {
&::after {
content: ', ';
}
&:last-of-type::after {
content: none;
}
}
/* DEFINITION LIST */ /* DEFINITION LIST */
.deflist { .deflist {
display: grid; display: grid;

View File

@ -11,7 +11,6 @@
autoDecay = parseInt(dataDecay, 10); autoDecay = parseInt(dataDecay, 10);
} }
closeEl.classList.add('alert__close'); closeEl.classList.add('alert__close');
closeEl.innerText = #{String (messageRender MsgCloseAlert)};
closeEl.addEventListener('click', function(event) { closeEl.addEventListener('click', function(event) {
alertEl.classList.add('alert--invisible'); alertEl.classList.add('alert--invisible');
}); });

View File

@ -72,6 +72,7 @@
.alert { .alert {
margin-left: 80px; margin-left: 80px;
max-width: 420px;
} }
} }

View File

@ -8,29 +8,45 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var elements = Array.from(document.querySelectorAll('.js-show-hide__toggle')), var LSNAME = 'SHOW_HIDE';
toggles = [];
function addEventHandler(el) { function addEventHandler(el) {
el.addEventListener('click', function elClickListener() { el.addEventListener('click', function elClickListener() {
var toggle = toggles[el.dataset.index]; var newState = el.parentElement.classList.toggle('js-show-hide--collapsed');
toggle.collapsed = !toggle.collapsed; updateLSState(el.dataset.shIndex || null, newState);
toggle.parent.classList.toggle('js-show-hide--collapsed', toggle.collapsed);
}); });
} }
elements.forEach(function(el, i) { function updateLSState(index, state) {
el.dataset.index = i; if (!index) {
var coll = el.dataset.collapsed === 'true'; return false;
if (coll) {
el.parentElement.classList.add('js-show-hide--collapsed')
} }
Array.from(el.parentElement.children).forEach(function(el) { var lsData = fromLocalStorage();
if (!el.classList.contains('js-show-hide__toggle')) { lsData[index] = state;
el.classList.add('js-show-hide__target'); window.localStorage.setItem(LSNAME, JSON.stringify(lsData));
} }
});
toggles.push({index: i, collapsed: coll, parent: el.parentElement}); function collapsedStateInLocalStorage(index) {
addEventHandler(el); return fromLocalStorage()[index] || null;
}
function fromLocalStorage() {
return JSON.parse(window.localStorage.getItem(LSNAME)) || {};
}
Array
.from(document.querySelectorAll('.js-show-hide__toggle'))
.forEach(function(el) {
var index = el.dataset.shIndex || null;
el.parentElement.classList.toggle(
'js-show-hide--collapsed',
collapsedStateInLocalStorage(index) || el.dataset.collapsed === 'true'
);
Array.from(el.parentElement.children).forEach(function(el) {
if (!el.classList.contains('js-show-hide__toggle')) {
el.classList.add('js-show-hide__target');
}
});
addEventHandler(el);
}); });
}); });

View File

@ -2,14 +2,14 @@ $newline never
<aside .main__aside> <aside .main__aside>
<div .asidenav> <div .asidenav>
$forall tid@TermIdentifier{..} <- favouriteTerms $forall tid@TermIdentifier{..} <- favouriteTerms
<div .asidenav__box> <div .asidenav__box.js-show-hide>
<h3 .asidenav__box-title.js-show-hide__toggle> <h3 .asidenav__box-title.js-show-hide__toggle data-sh-index="#{display season}-#{year}">
$case season $case season
$of Winter $of Winter
_{MsgWinterTermShort year} _{MsgWinterTermShort year}
$of Summer $of Summer
_{MsgSummerTermShort year} _{MsgSummerTermShort year}
<ul .asidenav__list> <ul .asidenav__list.js-show-hide__target>
$forall (Course{..}, courseRoute, pageActions) <- favouriteTerm tid $forall (Course{..}, courseRoute, pageActions) <- favouriteTerm tid
<li .asidenav__list-item :highlight courseRoute:.asidenav__list-item--active> <li .asidenav__list-item :highlight courseRoute:.asidenav__list-item--active>
<a .asidenav__link-wrapper href=@{courseRoute}> <a .asidenav__link-wrapper href=@{courseRoute}>

View File

@ -19,15 +19,21 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var asidenavEl = document.querySelector('.main__aside'); var asidenavEl = document.querySelector('.main__aside');
var mainEl = document.querySelector('.main__content'); var mainContentEl = document.querySelector('.main__content');
asidenavEl.style.height = `${mainEl.clientHeight + 75}px`; function adjustHeight() {
window.addEventListener('resize', function() {
window.requestAnimationFrame(function() { window.requestAnimationFrame(function() {
asidenavEl.style.height = `${mainEl.clientHeight + 75}px`; asidenavEl.style.height = mainContentEl.clientHeight + 'px';
}); });
}); }
// unbeknownst to the user (below the fold), this happes slightly delayed
// because of dynamic changes to the styles inside the main__content
setTimeout(function() {
adjustHeight();
}, 10);
window.addEventListener('resize', adjustHeight);
window.utils.aside(asidenavEl); window.utils.aside(asidenavEl);

View File

@ -16,6 +16,13 @@
} }
} }
@media (max-width: 768px) {
.main__aside {
min-height: calc(100% - var(--header-height-collapsed));
}
}
@media (max-width: 425px) { @media (max-width: 425px) {
.main__aside { .main__aside {