diff --git a/app/Export.hs b/app/Export.hs
index 20d2975..253c8e0 100644
--- a/app/Export.hs
+++ b/app/Export.hs
@@ -42,7 +42,8 @@ module Export where
"target" .= values ! "target",
"actionData" .= object [
"mode" .= values ! "mode",
- "actors" .= values ! "actors"]] : result
+ "actors" .= values ! "actors",
+ "viewers" .= values ! "viewers"]] : result
instance ToJSON GraphData where
toJSON (GData (nd, ed)) = object ["states" .= toJSON nd, "actions" .= toJSON ed]
diff --git a/app/Workflow.hs b/app/Workflow.hs
index 268fe86..a7e7eef 100644
--- a/app/Workflow.hs
+++ b/app/Workflow.hs
@@ -87,7 +87,7 @@ module Workflow where
name :: Maybe Label,
actors :: Maybe [Map String Value],
viewActor :: Maybe Value,
- viewers :: Maybe Value,
+ viewers :: Maybe [Map String Value],
messages :: Maybe Value,
form :: Maybe Value
} deriving (Show, Generic)
@@ -155,7 +155,8 @@ module Workflow where
("source", Single source),
("target", Single targetID),
("mode", Single mode),
- ("actors", List $ Prelude.map Dict actors)] where
+ ("actors", List $ Prelude.map Dict actors),
+ ("viewers", List $ Prelude.map Dict viewers)] where
name = if isNothing a.name
then ident
else case (fromJust a.name).fallback of
@@ -164,5 +165,6 @@ module Workflow where
source = fromMaybe initID a.source
mode = fromMaybe "" a.mode
actors = fromMaybe [] a.actors
+ viewers = fromMaybe [] a.viewers
---------------------------------------
\ No newline at end of file
diff --git a/editor.css b/editor.css
index 3ab857d..1af35c1 100644
--- a/editor.css
+++ b/editor.css
@@ -4,4 +4,30 @@
#editor {
border: 10px solid red;
-} */
\ No newline at end of file
+} */
+
+body {
+ margin: 0 0 0 0;
+}
+
+#settings {
+ line-height: 2;
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
+ top: 0px;
+ width: 100%;
+ position: fixed;
+ padding: 8 8 8 8;
+ background-color: rgb(230, 230, 230);
+ z-index: 2;
+ float: left;
+ overflow: auto;
+ align-items: center;
+}
+
+#settings div {
+ width: fit-content;
+ position: relative;
+ overflow: hidden;
+ float: left;
+ margin-right: 20;
+}
\ No newline at end of file
diff --git a/editor.html b/editor.html
index 4d96b96..4d74269 100644
--- a/editor.html
+++ b/editor.html
@@ -8,17 +8,22 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/editor.js b/editor.js
index 2c8a129..d86abdd 100644
--- a/editor.js
+++ b/editor.js
@@ -11,49 +11,122 @@ var workflow = {}
var stateIdCounter = workflow.states ? workflow.states.length : 0;
var actionIdCounter = workflow.states ? workflow.actions.length : 0;
-//Persons & roles of the workflow
+//Actors of the workflow
var actors = [];
workflow.actions.forEach(act => act.actionData.actors.forEach(a => {
var includes = false;
actors.forEach(actor => includes = includes || equalRoles(a, actor));
(!includes) && actors.push(a);
(!act.actionData.actorNames) && (act.actionData.actorNames = []);
- act.actionData.actorNames.push(getActorName(a));
+ act.actionData.actorNames.push(getRoleName(a));
}));
// console.log(actors);
// workflow.actions.forEach(a => console.log(a.actionData.actorNames));
-function getActorName(actor) {
- if (actor.tag == 'payload-reference') {
- return actor['payload-label'];
- } else if (actor.authorized) {
- return actor.authorized['dnf-terms'][0][0].var + ' (auth)';
+function getRoleName(role) {
+ if (typeof role == 'string') {
+ return role;
+ } else if (role.tag == 'payload-reference') {
+ return role['payload-label'];
+ } else if (role.authorized) {
+ return role.authorized['dnf-terms'][0][0].var + ' (auth)';
} else {
- return actor.user;
- }
-
+ return role.user || JSON.stringify(role);
+ }
}
+const NO_ACTOR = 'None';
+
//Prepare actor highlighting
const selectedActor = document.getElementById('actor');
var allActors = document.createElement('option');
-allActors.text = 'All Actors';
+allActors.text = NO_ACTOR;
selectedActor.add(allActors);
actors.forEach(actor => {
var option = document.createElement('option');
- option.text = getActorName(actor);
+ option.text = getRoleName(actor);
selectedActor.add(option);
});
+//Viewers of the workflow
+var viewers = [];
+//Actions/States with no explicit viewers
+var viewableByAll = []
+//Possible initiators
+var initiators = []
+//Implicit state from which initial actions can be selected
+var initState = null;
+//Identify all viewers of every action
+workflow.actions.forEach(act => {
+ if (act.actionData.viewers.length === 0) {
+ viewableByAll.push(act.actionData);
+ } else {
+ act.actionData.viewers.forEach(v => {
+ var includes = false;
+ viewers.forEach(viewer => includes = includes || equalRoles(v, viewer));
+ (!includes) && viewers.push(v);
+ (!act.actionData.viewerNames) && (act.actionData.viewerNames = []);
+ act.actionData.viewerNames.push(getRoleName(v));
+ })
+ }
+ if (act.actionData.mode === 'initial') {
+ act.actionData.actorNames.forEach(an => !initiators.includes(an) && initiators.push(an));
+ }
+});
+//Identify all viewers of every state
+workflow.states.forEach(st => {
+ if (st.name === '@@INIT') {
+ initState = st;
+ } else if (st.stateData.viewers.length === 0) {
+ viewableByAll.push(st.stateData);
+ } else {
+ st.stateData.viewers.forEach(v => {
+ var includes = false;
+ viewers.forEach(viewer => includes = includes || equalRoles(v, viewer));
+ (!includes) && viewers.push(v);
+ (!st.stateData.viewerNames) && (st.stateData.viewerNames = []);
+ st.stateData.viewerNames.push(getRoleName(v));
+ })
+ }
+});
+
+initState.stateData.viewerNames = initiators;
+
+const ALL_VIEW = "All Roles";
+if (viewableByAll.length > 0) {
+ viewers.push(ALL_VIEW);
+ var viewerNames = []
+ viewers.forEach(viewer => viewerNames.push(getRoleName(viewer)));
+ viewableByAll.forEach(data => {
+ data.viewerNames = viewerNames;
+ });
+}
+
+
+const NO_VIEWER = NO_ACTOR;
+
+//Prepare viewer highlighting
+const selectedViewer = document.getElementById('viewer');
+var allViewers = document.createElement('option');
+allViewers.text = NO_VIEWER;
+selectedViewer.add(allViewers);
+viewers.forEach(viewer => {
+ var option = document.createElement('option');
+ option.text = getRoleName(viewer);
+ selectedViewer.add(option);
+});
+
+
//source & target nodes of all currently highlighted actions
var highlightedSources = [];
var highlightedTargets = [];
function selectActor() {
- console.log(selectedActor.value);
+ // console.log(selectedActor.value);
highlightedSources = [];
highlightedTargets = [];
+ selectedViewer.value = NO_VIEWER;
workflow.actions.forEach(act => {
if (act.actionData.mode != 'automatic' && act.actionData.actorNames.includes(selectedActor.value)) {
highlightedSources.push(act.source.id);
@@ -62,6 +135,17 @@ function selectActor() {
});
}
+function selectViewer() {
+ highlightedSources = [];
+ highlightedTargets = [];
+ selectedActor.value = NO_ACTOR;
+ workflow.states.forEach(st => {
+ if (st.stateData.viewerNames.includes(selectedViewer.value)) {
+ highlightedSources.push(st.id);
+ }
+ });
+}
+
var selfLoops = {}; // All edges whose targets equal their sources.
var overlappingEdges = {}; // All edges whose target and source are connected by further.
@@ -221,7 +305,8 @@ function removeState(state) {
* @returns The colour the given node should have.
*/
function getNodeColour(node) {
- var standard = selectedActor.value === 'All Actors' || highlightedSources.includes(node.id) || highlightedTargets.includes(node.id)
+ var standard = (selectedActor.value === NO_ACTOR && selectedViewer.value === NO_VIEWER)
+ || highlightedSources.includes(node.id) || highlightedTargets.includes(node.id)
var alpha = standard ? 'ff' : '55';
if (node.stateData && node.stateData.final !== 'False' && node.stateData.final !== '') {
if (node.stateData.final === 'True' || node.stateData.final === 'ok') {
@@ -239,13 +324,15 @@ function getNodeColour(node) {
}
function isHighlightedEdge(edge) {
- return (edge.actionData.mode != 'automatic' && edge.actionData.actorNames.includes(selectedActor.value)) || (edge.actionData.mode === 'automatic' && highlightedTargets.includes(edge.source.id));
+ var data = edge.actionData
+ var selectedRole = data.mode != 'automatic' && (data.actorNames.includes(selectedActor.value) || data.viewerNames.includes(selectedViewer.value))
+ return selectedRole || (data.mode === 'automatic' && highlightedTargets.includes(edge.source.id));
}
function getEdgeColour(edge) {
if (isHighlightedEdge(edge)) {
return selection === edge ? edgeColourHighlightSelected : edgeColourHighlightDefault;
- } else if (selectedActor.value !== 'All Actors') {
+ } else if (selectedActor.value !== NO_ACTOR) {
return selection === edge ? edgeColourSubtleSelected : edgeColourSubtleDefault;
} else {
return selection === edge ? edgeColourSelected : edgeColourDefault;