Merge branch 'master' into 624-case-insensitive-suche-nach-ident-email-in-usersr
This commit is contained in:
commit
9a5e53c648
14
CHANGELOG.md
14
CHANGELOG.md
@ -2,6 +2,20 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [25.22.4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.22.3...v25.22.4) (2021-10-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **routes:** make access to workflows free ([29c54db](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/29c54db06f01659a3a6419009964a85cd11d5441))
|
||||
|
||||
## [25.22.3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.22.2...v25.22.3) (2021-10-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **navigation:** always link workflows nav to instances ([adf9709](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/adf9709567d9a320f2c17d3c5dde940c2f9d8862))
|
||||
|
||||
## [25.22.2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.22.1...v25.22.2) (2021-10-13)
|
||||
|
||||
## [25.22.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v25.22.0...v25.22.1) (2021-10-02)
|
||||
|
||||
22
frontend/src/lib/tooltips/frontend-tooltips.js
Normal file
22
frontend/src/lib/tooltips/frontend-tooltips.js
Normal file
@ -0,0 +1,22 @@
|
||||
export class FrontendTooltips {
|
||||
|
||||
static addToolTip(element, text) {
|
||||
let tooltipWrap = document.createElement('span');
|
||||
tooltipWrap.className = 'tooltip';
|
||||
|
||||
let tooltipContent = document.createElement('span');
|
||||
tooltipContent.className = 'tooltip__content';
|
||||
tooltipContent.appendChild(document.createTextNode(text));
|
||||
tooltipWrap.append(tooltipContent);
|
||||
|
||||
let tooltipHandle = document.createElement('span');
|
||||
tooltipHandle.className = 'tooltip__handle';
|
||||
let icon = document.createElement('i');
|
||||
icon.classList.add('fas');
|
||||
icon.classList.add('fa-question-circle');
|
||||
tooltipHandle.append(icon);
|
||||
tooltipWrap.append(tooltipHandle);
|
||||
|
||||
element.append(tooltipWrap);
|
||||
}
|
||||
}
|
||||
17
frontend/src/messages.js
Normal file
17
frontend/src/messages.js
Normal file
@ -0,0 +1,17 @@
|
||||
export class Translations {
|
||||
static translations = {
|
||||
'checkrangeTooltip' : {
|
||||
'de' : 'Shift-Klick, um mehrere Zellen zu markieren.',
|
||||
'en' : 'Shift-click to mark multiple cells.',
|
||||
},
|
||||
};
|
||||
|
||||
static getTranslation(key, language) {
|
||||
let json = Translations.translations[key];
|
||||
if(language === 'en') {
|
||||
return json.en;
|
||||
} else {
|
||||
return json.de;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -21,6 +21,8 @@ export class CheckAll {
|
||||
|
||||
_tableIndices;
|
||||
|
||||
_lastCheckedCell = null;
|
||||
|
||||
constructor(element, app) {
|
||||
if (!element) {
|
||||
throw new Error('Check All utility cannot be setup without an element!');
|
||||
@ -41,7 +43,9 @@ export class CheckAll {
|
||||
if (DEBUG_MODE > 0)
|
||||
console.log(this._columns);
|
||||
|
||||
this._findCheckboxColumns().forEach(columnId => this._checkAllColumns.push(new CheckAllColumn(this._element, app, this._columns[columnId], this._eventManager)));
|
||||
let checkboxColumns = this._findCheckboxColumns();
|
||||
|
||||
checkboxColumns.forEach(columnId => this._checkAllColumns.push(new CheckAllColumn(this._element, app, this._columns[columnId])));
|
||||
|
||||
// mark initialized
|
||||
this._element.classList.add(CHECK_ALL_INITIALIZED_CLASS);
|
||||
@ -116,6 +120,7 @@ class CheckAllColumn {
|
||||
this._checkAllCheckbox = document.createElement('input');
|
||||
this._checkAllCheckbox.setAttribute('type', 'checkbox');
|
||||
this._checkAllCheckbox.setAttribute('id', this._checkboxId);
|
||||
|
||||
th.insertBefore(this._checkAllCheckbox, th.firstChild);
|
||||
|
||||
// set up new checkbox
|
||||
|
||||
125
frontend/src/utils/inputs/checkrange.js
Normal file
125
frontend/src/utils/inputs/checkrange.js
Normal file
@ -0,0 +1,125 @@
|
||||
import { Utility } from '../../core/utility';
|
||||
import { TableIndices } from '../../lib/table/table';
|
||||
import { FrontendTooltips } from '../../lib/tooltips/frontend-tooltips';
|
||||
import { Translations } from '../../messages';
|
||||
|
||||
|
||||
const CHECKRANGE_INITIALIZED_CLASS = 'checkrange--initialized';
|
||||
const CHECKBOX_SELECTOR = '[type="checkbox"]';
|
||||
|
||||
|
||||
@Utility({
|
||||
selector: 'table:not([uw-no-check-all])',
|
||||
})
|
||||
export class CheckRange {
|
||||
_lastCheckedCell = null;
|
||||
_element;
|
||||
_tableIndices
|
||||
_columns = new Array();
|
||||
|
||||
constructor(element) {
|
||||
if(!element) {
|
||||
throw new Error('Check Range Utility cannot be setup without an element');
|
||||
}
|
||||
|
||||
this._element = element;
|
||||
|
||||
if (this._element.classList.contains(CHECKRANGE_INITIALIZED_CLASS))
|
||||
return false;
|
||||
|
||||
this._tableIndices = new TableIndices(this._element);
|
||||
|
||||
this._gatherColumns();
|
||||
|
||||
let checkboxColumns = this._findCheckboxColumns();
|
||||
|
||||
checkboxColumns.forEach(columnId => this._setUpShiftClickOnColumn(columnId));
|
||||
|
||||
this._element.classList.add(CHECKRANGE_INITIALIZED_CLASS);
|
||||
}
|
||||
|
||||
_setUpShiftClickOnColumn(columnId) {
|
||||
if (!this._columns || columnId < 0 || columnId >= this._columns.length) return;
|
||||
let column = this._columns[columnId];
|
||||
let language = document.documentElement.lang;
|
||||
let toolTipMessage = Translations.getTranslation('checkrangeTooltip', language);
|
||||
FrontendTooltips.addToolTip(column[0], toolTipMessage);
|
||||
column.forEach(el => el.addEventListener('click', (ev) => {
|
||||
|
||||
if(ev.shiftKey && this.lastCheckedCell !== null) {
|
||||
let lastClickedIndex = this._tableIndices.rowIndex(this._lastCheckedCell);
|
||||
let currentCellIndex = this._tableIndices.rowIndex(el);
|
||||
let cell = this._columns[columnId][currentCellIndex];
|
||||
if(currentCellIndex > lastClickedIndex)
|
||||
this._handleCellsInBetween(cell, lastClickedIndex, currentCellIndex, columnId);
|
||||
else
|
||||
this._handleCellsInBetween(cell, currentCellIndex, lastClickedIndex, columnId);
|
||||
} else {
|
||||
this._lastCheckedCell = el;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
_handleCellsInBetween(cell, firstRowIndex, lastRowIndex, columnId) {
|
||||
if(this._isChecked(cell)) {
|
||||
this._uncheckMultipleCells(firstRowIndex, lastRowIndex, columnId);
|
||||
} else {
|
||||
this._checkMultipleCells(firstRowIndex, lastRowIndex, columnId);
|
||||
}
|
||||
}
|
||||
|
||||
_checkMultipleCells(firstRowIndex, lastRowIndex, columnId) {
|
||||
for(let i=firstRowIndex; i<=lastRowIndex; i++) {
|
||||
let cell = this._columns[columnId][i];
|
||||
if (cell.tagName !== 'TH') {
|
||||
cell.querySelector(CHECKBOX_SELECTOR).checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_uncheckMultipleCells(firstRowIndex, lastRowIndex, columnId) {
|
||||
for(let i=firstRowIndex; i<=lastRowIndex; i++) {
|
||||
let cell = this._columns[columnId][i];
|
||||
if (cell.tagName !== 'TH') {
|
||||
cell.querySelector(CHECKBOX_SELECTOR).checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_isChecked(cell) {
|
||||
return cell.querySelector(CHECKBOX_SELECTOR).checked;
|
||||
}
|
||||
|
||||
|
||||
_gatherColumns() {
|
||||
for (const rowIndex of Array(this._tableIndices.maxRow + 1).keys()) {
|
||||
for (const colIndex of Array(this._tableIndices.maxCol + 1).keys()) {
|
||||
|
||||
const cell = this._tableIndices.getCell(rowIndex, colIndex);
|
||||
|
||||
if (!cell)
|
||||
continue;
|
||||
|
||||
if (!this._columns[colIndex])
|
||||
this._columns[colIndex] = new Array();
|
||||
|
||||
this._columns[colIndex][rowIndex] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_findCheckboxColumns() {
|
||||
let checkboxColumnIds = new Array();
|
||||
this._columns.forEach((col, i) => {
|
||||
if (this._isCheckboxColumn(col)) {
|
||||
checkboxColumnIds.push(i);
|
||||
}
|
||||
});
|
||||
return checkboxColumnIds;
|
||||
}
|
||||
|
||||
_isCheckboxColumn(col) {
|
||||
return col.every(cell => cell.tagName == 'TH' || cell.querySelector(CHECKBOX_SELECTOR))
|
||||
&& col.some(cell => cell.querySelector(CHECKBOX_SELECTOR));
|
||||
}
|
||||
}
|
||||
5
frontend/src/utils/inputs/checkrange.md
Normal file
5
frontend/src/utils/inputs/checkrange.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Checkrange Utility
|
||||
Is set on the table header of a specific row. Remembers the last checked checkbox. When the users shift-clicks another checkbox in the same row, all checkboxes in between are also checked.
|
||||
|
||||
# Attribute: table:not([uw-no-check-all]
|
||||
(will be setup on all tables which use the util check-all)
|
||||
@ -2,6 +2,7 @@ import { Checkbox } from './checkbox';
|
||||
import { FileInput } from './file-input';
|
||||
import { FileMaxSize } from './file-max-size';
|
||||
import { Password } from './password';
|
||||
import { CheckRange } from './checkrange';
|
||||
|
||||
import './inputs.sass';
|
||||
import './radio-group.sass';
|
||||
@ -11,4 +12,5 @@ export const InputUtils = [
|
||||
FileInput,
|
||||
FileMaxSize,
|
||||
Password,
|
||||
CheckRange,
|
||||
];
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uni2work",
|
||||
"version": "25.22.2",
|
||||
"version": "25.22.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uni2work",
|
||||
"version": "25.22.2",
|
||||
"version": "25.22.4",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
name: uniworx
|
||||
version: 25.22.2
|
||||
version: 25.22.4
|
||||
dependencies:
|
||||
- base
|
||||
- yesod
|
||||
|
||||
4
routes
4
routes
@ -78,7 +78,7 @@
|
||||
/global-workflows/instances/#WorkflowInstanceName GlobalWorkflowInstanceR:
|
||||
/edit GWIEditR GET POST
|
||||
/delete GWIDeleteR GET POST
|
||||
/workflows GWIWorkflowsR GET !¬empty
|
||||
/workflows GWIWorkflowsR GET !free
|
||||
/initiate GWIInitiateR GET POST !workflow
|
||||
/update GWIUpdateR POST
|
||||
/global-workflows GlobalWorkflowWorkflowListR GET !free
|
||||
@ -145,7 +145,7 @@
|
||||
/workflows/instances/#WorkflowInstanceName SchoolWorkflowInstanceR:
|
||||
/edit SWIEditR GET POST
|
||||
/delete SWIDeleteR GET POST
|
||||
/workflows SWIWorkflowsR GET !¬empty
|
||||
/workflows SWIWorkflowsR GET !free
|
||||
/initiate SWIInitiateR GET POST !workflow
|
||||
/update SWIUpdateR POST
|
||||
/workflows SchoolWorkflowWorkflowListR GET !free
|
||||
|
||||
@ -573,8 +573,8 @@ navLinkAccess NavLink{..} = case navAccess' of
|
||||
|
||||
defaultLinks :: ( MonadHandler m
|
||||
, HandlerSite m ~ UniWorX
|
||||
, MonadThrow m
|
||||
, WithRunDB SqlReadBackend (HandlerFor UniWorX) m
|
||||
-- , MonadThrow m
|
||||
-- , WithRunDB SqlReadBackend (HandlerFor UniWorX) m
|
||||
, BearerAuthSite UniWorX
|
||||
) => m [Nav]
|
||||
defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the header.
|
||||
@ -761,12 +761,14 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the
|
||||
, do
|
||||
guardVolatile clusterVolatileWorkflowsEnabled
|
||||
|
||||
authCtx <- getAuthContext
|
||||
(haveInstances, haveWorkflows) <- lift . memcachedBy (Just . Right $ 2 * diffMinute) (NavCacheHaveTopWorkflowsInstances authCtx) . useRunDB $ (,)
|
||||
<$> haveTopWorkflowInstances
|
||||
<*> haveTopWorkflowWorkflows
|
||||
-- authCtx <- getAuthContext
|
||||
-- (haveInstances, haveWorkflows) <- lift . memcachedBy (Just . Right $ 2 * diffMinute) (NavCacheHaveTopWorkflowsInstances authCtx) . useRunDB $ (,)
|
||||
-- <$> haveTopWorkflowInstances
|
||||
-- <*> haveTopWorkflowWorkflows
|
||||
|
||||
if | haveInstances -> return NavHeader
|
||||
mUserId <- maybeAuthId
|
||||
-- if | haveInstances -> return NavHeader
|
||||
if | isJust mUserId -> return NavHeader
|
||||
{ navHeaderRole = NavHeaderPrimary
|
||||
, navIcon = IconMenuWorkflows
|
||||
, navLink = NavLink
|
||||
@ -778,18 +780,18 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the
|
||||
, navForceActive = False
|
||||
}
|
||||
}
|
||||
| haveWorkflows -> return NavHeader
|
||||
{ navHeaderRole = NavHeaderPrimary
|
||||
, navIcon = IconMenuWorkflows
|
||||
, navLink = NavLink
|
||||
{ navLabel = MsgMenuTopWorkflowWorkflowListHeader
|
||||
, navRoute = TopWorkflowWorkflowListR
|
||||
, navAccess' = NavAccessTrue
|
||||
, navType = NavTypeLink { navModal = False }
|
||||
, navQuick' = mempty
|
||||
, navForceActive = False
|
||||
}
|
||||
}
|
||||
-- | haveWorkflows -> return NavHeader
|
||||
-- { navHeaderRole = NavHeaderPrimary
|
||||
-- , navIcon = IconMenuWorkflows
|
||||
-- , navLink = NavLink
|
||||
-- { navLabel = MsgMenuTopWorkflowWorkflowListHeader
|
||||
-- , navRoute = TopWorkflowWorkflowListR
|
||||
-- , navAccess' = NavAccessTrue
|
||||
-- , navType = NavTypeLink { navModal = False }
|
||||
-- , navQuick' = mempty
|
||||
-- , navForceActive = False
|
||||
-- }
|
||||
-- }
|
||||
| otherwise -> mzero
|
||||
, return NavHeaderContainer
|
||||
{ navHeaderRole = NavHeaderPrimary
|
||||
@ -2730,34 +2732,35 @@ haveWorkflowWorkflows rScope = hoist liftHandler . withReaderT (projectBackend @
|
||||
|
||||
lift $ anyM roles evalRole
|
||||
|
||||
haveTopWorkflowInstances, haveTopWorkflowWorkflows
|
||||
-- haveTopWorkflowInstances,
|
||||
haveTopWorkflowWorkflows
|
||||
:: ( MonadHandler m, HandlerSite m ~ UniWorX
|
||||
, BackendCompatible SqlReadBackend backend
|
||||
, BearerAuthSite UniWorX
|
||||
)
|
||||
=> ReaderT backend m Bool
|
||||
haveTopWorkflowInstances = hoist liftHandler . withReaderT (projectBackend @SqlReadBackend) . $cachedHere . maybeT (return False) $ do
|
||||
roles <- memcachedBy @(Set ((RouteWorkflowScope, WorkflowInstanceName), WorkflowRole UserId)) (Just $ Right diffDay) NavCacheHaveTopWorkflowInstancesRoles $ do
|
||||
let
|
||||
getInstances = E.selectSource . E.from $ \workflowInstance -> do
|
||||
E.where_ . isTopWorkflowScopeSql $ workflowInstance E.^. WorkflowInstanceScope
|
||||
return workflowInstance
|
||||
instanceRoles (Entity _ WorkflowInstance{..}) = do
|
||||
rScope <- toRouteWorkflowScope $ _DBWorkflowScope # workflowInstanceScope
|
||||
wiGraph <- lift $ getSharedIdWorkflowGraph workflowInstanceGraph
|
||||
return . Set.mapMonotonic ((rScope, workflowInstanceName), ) . fold $ do
|
||||
WGN{..} <- wiGraph ^.. _wgNodes . folded
|
||||
WorkflowGraphEdgeInitial{..} <- wgnEdges ^.. folded
|
||||
return wgeActors
|
||||
runConduit $ transPipe lift getInstances .| C.foldMapM instanceRoles
|
||||
|
||||
let
|
||||
evalRole :: _ -> ReaderT SqlReadBackend (HandlerFor UniWorX) Bool
|
||||
evalRole ((rScope, win), role) = do
|
||||
let route = _WorkflowScopeRoute # (rScope, WorkflowInstanceR win WIInitiateR)
|
||||
is _Authorized <$> hasWorkflowRole Nothing role route False
|
||||
|
||||
lift $ anyM roles evalRole
|
||||
-- haveTopWorkflowInstances = hoist liftHandler . withReaderT (projectBackend @SqlReadBackend) . $cachedHere . maybeT (return False) $ do
|
||||
-- roles <- memcachedBy @(Set ((RouteWorkflowScope, WorkflowInstanceName), WorkflowRole UserId)) (Just $ Right diffDay) NavCacheHaveTopWorkflowInstancesRoles $ do
|
||||
-- let
|
||||
-- getInstances = E.selectSource . E.from $ \workflowInstance -> do
|
||||
-- E.where_ . isTopWorkflowScopeSql $ workflowInstance E.^. WorkflowInstanceScope
|
||||
-- return workflowInstance
|
||||
-- instanceRoles (Entity _ WorkflowInstance{..}) = do
|
||||
-- rScope <- toRouteWorkflowScope $ _DBWorkflowScope # workflowInstanceScope
|
||||
-- wiGraph <- lift $ getSharedIdWorkflowGraph workflowInstanceGraph
|
||||
-- return . Set.mapMonotonic ((rScope, workflowInstanceName), ) . fold $ do
|
||||
-- WGN{..} <- wiGraph ^.. _wgNodes . folded
|
||||
-- WorkflowGraphEdgeInitial{..} <- wgnEdges ^.. folded
|
||||
-- return wgeActors
|
||||
-- runConduit $ transPipe lift getInstances .| C.foldMapM instanceRoles
|
||||
--
|
||||
-- let
|
||||
-- evalRole :: _ -> ReaderT SqlReadBackend (HandlerFor UniWorX) Bool
|
||||
-- evalRole ((rScope, win), role) = do
|
||||
-- let route = _WorkflowScopeRoute # (rScope, WorkflowInstanceR win WIInitiateR)
|
||||
-- is _Authorized <$> hasWorkflowRole Nothing role route False
|
||||
--
|
||||
-- lift $ anyM roles evalRole
|
||||
haveTopWorkflowWorkflows = hoist liftHandler . withReaderT (projectBackend @SqlReadBackend) . $cachedHere . maybeT (return False) $ do
|
||||
roles <- memcachedBy (Just $ Right diffDay) NavCacheHaveTopWorkflowWorkflowsRoles $ do
|
||||
let
|
||||
|
||||
Reference in New Issue
Block a user