Merge branch 'master' into staging
This commit is contained in:
commit
69c2e95d83
1
models
1
models
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -72,6 +72,7 @@
|
|||||||
|
|
||||||
.alert {
|
.alert {
|
||||||
margin-left: 80px;
|
margin-left: 80px;
|
||||||
|
max-width: 420px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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}>
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user