Skip to content

Commit d0c04ad

Browse files
committed
all permissions logic implemented and transition add by drag implemented
1 parent b4dc905 commit d0c04ad

File tree

5 files changed

+120
-38
lines changed

5 files changed

+120
-38
lines changed

administrator/components/com_workflow/resources/scripts/components/canvas/WorkflowCanvas.vue

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
:edges="styledEdges"
1616
:node-types="nodeTypes"
1717
:edge-types="edgeTypes"
18-
:nodes-connectable="true"
18+
:nodes-connectable="workflow?.canCreate"
1919
:elements-selectable="true"
2020
:snap-to-grid="true"
2121
:snap-grid="[40, 40]"
@@ -30,17 +30,23 @@
3030
/>
3131

3232
<button
33+
id="toggle-minimap"
3334
class="toolbar-button custom-controls-button position-absolute"
35+
tabindex="0"
3436
style="z-index: 1003"
3537
:style="showMiniMap ? 'bottom: 130px; left: 180px;' : 'bottom: 10px; left: 10px;'"
36-
tabindex="0"
37-
@click="showMiniMap = !showMiniMap"
3838
:aria-label="showMiniMap ? translate('COM_WORKFLOW_GRAPH_MINIMAP_HIDE') : translate('COM_WORKFLOW_GRAPH_MINIMAP_SHOW')"
3939
:title="showMiniMap ? translate('COM_WORKFLOW_GRAPH_MINIMAP_HIDE') : translate('COM_WORKFLOW_GRAPH_MINIMAP_SHOW')"
40-
id="toggle-minimap"
40+
@click="showMiniMap = !showMiniMap"
4141
>
42-
<span v-if="showMiniMap" class="fa fa-close"></span>
43-
<span v-else class="icon icon-expand-2"></span>
42+
<span
43+
v-if="showMiniMap"
44+
class="fa fa-close"
45+
/>
46+
<span
47+
v-else
48+
class="icon icon-expand-2"
49+
/>
4450
</button>
4551
<MiniMap
4652
v-if="showMiniMap"
@@ -53,6 +59,7 @@
5359
/>
5460
<CustomControls aria-label="Graph controls" />
5561
<ControlsPanel
62+
v-if="workflow?.canCreate"
5663
class="canvas-controls-panel"
5764
:is-transition-mode="isTransitionMode"
5865
@add-stage="addStage"
@@ -71,7 +78,7 @@
7178

7279
<script>
7380
import {
74-
ref, computed, onMounted, onUnmounted, watch, nextTick,
81+
ref, computed, onMounted, onUnmounted, watch,
7582
} from 'vue';
7683
import { useStore } from 'vuex';
7784
// eslint-disable-next-line import/no-unresolved
@@ -113,7 +120,7 @@ export default {
113120
setup() {
114121
const store = useStore();
115122
const {
116-
fitView, zoomIn, zoomOut, viewport, zoomTo, setViewport, onViewportChange, getViewport,
123+
fitView, zoomIn, zoomOut, viewport, setViewport, onViewportChange,
117124
} = useVueFlow();
118125
119126
const isTransitionMode = ref(true);
@@ -125,6 +132,7 @@ export default {
125132
const previouslyFocusedElement = ref(null);
126133
127134
const showMiniMap = ref(true);
135+
const workflow = computed(() => store.getters.workflow || {});
128136
const stages = computed(() => store.getters.stages || []);
129137
const transitions = computed(() => store.getters.transitions || []);
130138
const loading = computed(() => store.getters.loading);
@@ -134,11 +142,18 @@ export default {
134142
return Joomla.Text._(key);
135143
}
136144
137-
function openModal(type, id = null) {
145+
function openModal(type, id = null, params = {}) {
138146
previouslyFocusedElement.value = document.activeElement;
139147
const extension = Joomla.getOptions('com_workflow', {})?.extension || '';
140148
const baseUrl = `index.php?option=com_workflow&view=${type}&workflow_id=${workflowId.value}&extension=${extension}&layout=modal&tmpl=component`;
141-
const src = id ? `${baseUrl}&id=${id}` : baseUrl;
149+
const baseUrlwithId = id ? `${baseUrl}&id=${id}` : baseUrl;
150+
const extraQuery = Object.entries(params)
151+
.map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`)
152+
.join('&');
153+
const src = extraQuery
154+
? `${baseUrlwithId}&${extraQuery}`
155+
: baseUrlwithId;
156+
142157
const textHeader = id
143158
? translate(`COM_WORKFLOW_GRAPH_EDIT_${type.toUpperCase()}`)
144159
: translate(`COM_WORKFLOW_GRAPH_ADD_${type.toUpperCase()}`);
@@ -152,6 +167,30 @@ export default {
152167
setupDialogFocusHandlers(previouslyFocusedElement, store);
153168
}
154169
170+
function canEdit(id, type = 'stage') {
171+
if (type === 'stage') {
172+
const stage = stages.value.find((s) => s.id === parseInt(id, 10));
173+
return stage?.permissions?.edit;
174+
}
175+
if (type === 'transition') {
176+
const transition = transitions.value.find((t) => t.id === parseInt(id, 10));
177+
return transition?.permissions?.edit;
178+
}
179+
return false;
180+
}
181+
182+
function canDelete(id, type = 'stage') {
183+
if (type === 'stage') {
184+
const stage = stages.value.find((s) => s.id === parseInt(id, 10));
185+
return stage?.permissions?.delete;
186+
}
187+
if (type === 'transition') {
188+
const transition = transitions.value.find((t) => t.id === parseInt(id, 10));
189+
return transition?.permissions?.delete;
190+
}
191+
return false;
192+
}
193+
155194
function selectStage(id) {
156195
selectedStage.value = parseInt(id, 10);
157196
selectedTransition.value = null;
@@ -163,10 +202,16 @@ export default {
163202
}
164203
165204
function editStage(id) {
205+
if (!canEdit(id, 'stage')) {
206+
return;
207+
}
166208
openModal('stage', id);
167209
}
168210
169211
function editTransition(id) {
212+
if (!canEdit(id, 'transition')) {
213+
return;
214+
}
170215
openModal('transition', id);
171216
}
172217
@@ -176,11 +221,17 @@ export default {
176221
}
177222
178223
function deleteStage(id) {
224+
if (!canDelete(id, 'stage')) {
225+
return;
226+
}
179227
store.dispatch('deleteStage', { id, workflowId: workflowId.value });
180228
selectedStage.value = null;
181229
}
182230
183231
function deleteTransition(id) {
232+
if (!canDelete(id, 'transition')) {
233+
return;
234+
}
184235
store.dispatch('deleteTransition', { id, workflowId: workflowId.value });
185236
selectedTransition.value = null;
186237
}
@@ -191,6 +242,9 @@ export default {
191242
}
192243
193244
function showDeleteModal(type, id) {
245+
if ((!canDelete(id, 'stage') && type === 'stage') || (!canDelete(id, 'transition') && type === 'transition')) {
246+
return;
247+
}
194248
const title = translate(type === 'stage'
195249
? 'COM_WORKFLOW_GRAPH_DELETE_STAGE_TITLE'
196250
: 'COM_WORKFLOW_GRAPH_DELETE_TRANSITION_TITLE');
@@ -207,11 +261,17 @@ export default {
207261
}
208262
209263
function addStage() {
264+
if (!workflow?.value?.canCreate) {
265+
return;
266+
}
210267
openModal('stage');
211268
announce(liveRegion.value, translate('COM_WORKFLOW_GRAPH_ADD_STAGE_DIALOG_OPENED'));
212269
}
213270
214271
function addTransition() {
272+
if (!workflow?.value?.canCreate) {
273+
return;
274+
}
215275
openModal('transition');
216276
announce(liveRegion.value, translate('COM_WORKFLOW_GRAPH_ADD_TRANSITION_DIALOG_OPENED'));
217277
}
@@ -226,8 +286,13 @@ export default {
226286
);
227287
}
228288
229-
function handleConnect() {
230-
if (isTransitionMode.value) openModal('transition');
289+
function handleConnect({ source, target }) {
290+
if (!workflow?.value?.canCreate) {
291+
return;
292+
}
293+
if (isTransitionMode.value && source && target) {
294+
openModal('transition', null, { from_stage_id: source, to_stage_id: target });
295+
}
231296
}
232297
233298
function selectEdge({ edge }) {
@@ -385,18 +450,16 @@ export default {
385450
const { panX, panY, zoom } = store.getters.canvas ?? {};
386451
387452
if (
388-
typeof panX === 'number' && !Number.isNaN(panX) &&
389-
typeof panY === 'number' && !Number.isNaN(panY) &&
390-
typeof zoom === 'number' && !Number.isNaN(zoom)
453+
typeof panX === 'number' && !Number.isNaN(panX)
454+
&& typeof panY === 'number' && !Number.isNaN(panY)
455+
&& typeof zoom === 'number' && !Number.isNaN(zoom)
391456
) {
392-
393457
isRestoringViewport = true;
394458
Promise.resolve()
395459
.then(() => setViewport({ x: panX, y: panY, zoom }))
396460
.finally(() => {
397461
isRestoringViewport = false;
398462
});
399-
400463
} else {
401464
fitView({ padding: 0.5, duration: 300 });
402465
}
@@ -408,19 +471,17 @@ export default {
408471
return;
409472
}
410473
411-
if ([x, y, zoom].some(v => typeof v !== 'number' || Number.isNaN(v))) {
474+
if ([x, y, zoom].some((v) => typeof v !== 'number' || Number.isNaN(v))) {
412475
return;
413476
}
414477
store.dispatch('updateCanvasViewport', { panX: x, panY: y, zoom });
415478
});
416479
417-
418-
419-
420480
return {
421481
loading,
422482
error,
423483
showMiniMap,
484+
workflow,
424485
positionedNodes,
425486
styledEdges,
426487
liveRegion,

administrator/components/com_workflow/resources/scripts/components/edges/CustomEdge.vue

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,43 @@
2323
@blur="onEdgeBlur"
2424
>
2525
<div class="d-flex flex-column border text-bg-primary">
26-
<div class="d-flex justify-content-end mb-1" v-if="data?.isTransitionMode">
26+
<div
27+
v-if="data?.isTransitionMode"
28+
class="d-flex justify-content-end mb-1 me-1 mt-1"
29+
>
2730
<button
31+
v-if="data?.permissions?.edit"
2832
class="btn btn-md btn-secondary py-0 px-1 me-1"
2933
:aria-label="translate('COM_WORKFLOW_GRAPH_EDIT_TRANSITION')"
3034
:title="translate('COM_WORKFLOW_GRAPH_EDIT_TRANSITION')"
3135
@click.stop="data?.onEdit?.()"
3236
>
33-
<i class="icon icon-edit" aria-hidden="true"/>
37+
<i
38+
class="icon icon-edit"
39+
aria-hidden="true"
40+
/>
3441
</button>
3542
<button
43+
v-if="data?.permissions?.delete"
3644
class="btn btn-md btn-danger py-0 px-1"
3745
:aria-label="translate('COM_WORKFLOW_GRAPH_DELETE_TRANSITION_TITLE')"
3846
:title="translate('COM_WORKFLOW_GRAPH_DELETE_TRANSITION_TITLE')"
3947
@click.stop="data?.onDelete?.()"
4048
>
41-
<i class="icon icon-trash" aria-hidden="true"/>
49+
<i
50+
class="icon icon-trash"
51+
aria-hidden="true"
52+
/>
4253
</button>
4354
</div>
44-
<!-- Label row below buttons, centered horizontally -->
4555
<div class="d-flex align-items-center rounded shadow-sm px-2 py-1 gap-1 flex-grow-1">
46-
<span
47-
class="h3 d-block text-truncate text-center text-white flex-grow-1 fw-semibold"
48-
:style="{ maxWidth: '100%' }"
49-
:title="data?.title"
50-
>
51-
{{ data?.title }}
52-
</span>
56+
<span
57+
class="h3 d-block text-truncate text-center text-white flex-grow-1 fw-semibold"
58+
:style="{ maxWidth: '100%' }"
59+
:title="data?.title"
60+
>
61+
{{ data?.title }}
62+
</span>
5363
</div>
5464
</div>
5565
</foreignObject>

administrator/components/com_workflow/resources/scripts/components/nodes/StageNode.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
:class="data?.isSpecial ? 'd-none' : 'd-flex'"
6060
>
6161
<button
62+
v-if="stage?.permissions?.edit"
6263
class="btn btn-lg btn-secondary py-0 px-1 me-2"
6364
:aria-label="translate('COM_WORKFLOW_GRAPH_EDIT_STAGE')"
6465
:title="translate('COM_WORKFLOW_GRAPH_EDIT_STAGE')"
@@ -70,6 +71,7 @@
7071
/>
7172
</button>
7273
<button
74+
v-if="stage?.permissions?.delete"
7375
class="btn btn-lg btn-danger py-0 px-1"
7476
:class="stage.default ? 'd-none' : ''"
7577
:aria-label="translate('COM_WORKFLOW_GRAPH_DELETE_STAGE_TITLE')"

administrator/components/com_workflow/src/Controller/GraphController.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public function getWorkflow(): void
142142
throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'));
143143
}
144144

145-
$canDo = ContentHelper::getActions($this->extension, 'workflow', $workflow->id);
145+
$canDo = ContentHelper::getActions($this->extension, 'workflow', $workflow->id);
146146
$canCreate = $canDo->get('core.create');
147147

148148
$response = [
@@ -152,7 +152,7 @@ public function getWorkflow(): void
152152
'published' => (bool) $workflow->published,
153153
'default' => (bool) $workflow->default,
154154
'extension' => $workflow->extension,
155-
'canCreate' => $canCreate,
155+
'canCreate' => $canCreate,
156156
];
157157

158158
echo new JsonResponse($response);
@@ -194,7 +194,7 @@ public function getStages()
194194
}
195195

196196
$response = [];
197-
$user = $this->app->getIdentity();
197+
$user = $this->app->getIdentity();
198198

199199
foreach ($stages as $stage) {
200200
$canEdit = $user->authorise('core.edit', $this->extension . '.stage.' . $stage->id);
@@ -207,7 +207,6 @@ public function getStages()
207207
'published' => (bool) $stage->published,
208208
'default' => (bool) $stage->default,
209209
'ordering' => (int) $stage->ordering,
210-
'editor' => $stage->editor,
211210
'position' => $stage->position ? json_decode($stage->position, true) : null,
212211
'workflow_id' => $stage->workflow_id,
213212
'permissions' => [
@@ -253,10 +252,10 @@ public function getTransitions()
253252
$transitions = $model->getItems();
254253

255254
$response = [];
256-
$user = $this->app->getIdentity();
255+
$user = $this->app->getIdentity();
257256

258257
foreach ($transitions as $transition) {
259-
$canEdit = $user->authorise('core.edit', $this->extension . '.transition.' . (int) $transition->id);
258+
$canEdit = $user->authorise('core.edit', $this->extension . '.transition.' . (int) $transition->id);
260259
$canDelete = $user->authorise('core.delete', $this->extension . '.transition.' . (int) $transition->id);
261260

262261
$response[] = [

administrator/components/com_workflow/tmpl/transition/edit.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@
4444
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'details', Text::_('COM_WORKFLOW_TRANSITION')); ?>
4545
<div class="row">
4646
<div class="col-lg-9">
47+
<?php
48+
if ($isModal) {
49+
$fromStage = $app->getInput()->get('from_stage_id');
50+
$toStage = $app->getInput()->get('to_stage_id');
51+
if ($fromStage && $toStage) {
52+
$this->form->setFieldAttribute('from_stage_id', 'default', $fromStage);
53+
$this->form->setFieldAttribute('to_stage_id', 'default', $toStage);
54+
}
55+
}
56+
?>
4757
<?php echo $this->form->renderField('from_stage_id'); ?>
4858
<?php echo $this->form->renderField('to_stage_id'); ?>
4959
<?php echo $this->form->renderField('description'); ?>

0 commit comments

Comments
 (0)