Skip to content
Open
Show file tree
Hide file tree
Changes from 92 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
80d1ffa
initial file setup for the workflow graphical view
Dileepadari Jun 10, 2025
29f35db
initial setup done with vue.js rendering
Dileepadari Jun 11, 2025
3a86a6e
menu options added in the UI
Dileepadari Jun 12, 2025
3bfb0ad
menu options added in the UI
Dileepadari Jun 12, 2025
9f2a264
menu options added in the UI
Dileepadari Jun 13, 2025
f918892
menu options added in the UI
Dileepadari Jun 13, 2025
3a57671
create the improved version of the UI
Dileepadari Jun 17, 2025
638815f
panel created with some collisions
Dileepadari Jun 18, 2025
d7e9e98
layout changed and made it consistent
Dileepadari Jun 19, 2025
b199e86
custom css added and enhanced canvas
Dileepadari Jun 20, 2025
1e3ff7c
added some functions and cleaned up code
Dileepadari Jun 20, 2025
211d03f
added stages to the canvas
Dileepadari Jun 21, 2025
ef15204
adding the initial version of transitions
Dileepadari Jun 23, 2025
eaa0ec0
added the transitions and the placement logic and initial setup of fu…
Dileepadari Jun 24, 2025
2ec20b8
added all the required functions and the getters and setter using aja…
Dileepadari Jun 25, 2025
9a7ded3
made Api setup and all the required getters and initial setup for the…
Dileepadari Jun 26, 2025
0106cd4
Added Undo and Redo Logic
Dileepadari Jun 26, 2025
22af2dd
Cleared errors in Api calls
Dileepadari Jun 27, 2025
77febbd
added modals for the add stage and transitions
Dileepadari Jun 27, 2025
6729a5b
stages and transitions modals connected to existing logic
Dileepadari Jun 30, 2025
693efae
positions fixed and undo, redo fixed
Dileepadari Jul 1, 2025
f802d55
delete stage completely executed
Dileepadari Jul 2, 2025
e1937d3
transition deletion and edit added and layout enhanced
Dileepadari Jul 2, 2025
239deee
Added Accessibility needs and shortcuts (To be Enhanced)
Dileepadari Jul 2, 2025
48504bf
some enhancements done
Dileepadari Jul 3, 2025
fff2f64
Save functionality and other validations done
Dileepadari Jul 3, 2025
8187d7d
updated the auto saving for positions, translations updated and the a…
Dileepadari Jul 6, 2025
69a901a
modal adjusted and some enhancements done
Dileepadari Jul 7, 2025
03f3fdd
layout changed and made code modular
Dileepadari Jul 8, 2025
06a36ad
improved the delete logic
Dileepadari Jul 8, 2025
1c38d65
refactored code without delete functionality
Dileepadari Jul 9, 2025
439e5d9
Merge remote-tracking branch 'origin/gsoc-workflow' into gsoc-workflo…
bembelimen Jul 9, 2025
5153db2
the modal logic enhnaced using joomla.dialog
Dileepadari Jul 9, 2025
c20e368
validation added for the html respose of delete api
Dileepadari Jul 9, 2025
542ad2c
validation added for the html respose of delete api
Dileepadari Jul 9, 2025
2113502
shortcuts added and validated
Dileepadari Jul 9, 2025
bfb0410
undo and redo added for position ajax call
Dileepadari Jul 9, 2025
6bcd4e1
landmarks for keyboard navigation improved
Dileepadari Jul 9, 2025
53f199d
github linting styles adjusted
Dileepadari Jul 10, 2025
70b642a
linting and css issues cleared
Dileepadari Jul 10, 2025
f098b47
some more refactoring
Dileepadari Jul 10, 2025
ae05b98
ceared lint issues again
Dileepadari Jul 10, 2025
1958429
adjusted css alignments
Dileepadari Jul 10, 2025
14fc988
all linting issues cleared
Dileepadari Jul 10, 2025
a799a07
css issues cleared and tab logic enhanced
Dileepadari Jul 15, 2025
4e50ea1
fit view adjusted and some css edits done
Dileepadari Jul 15, 2025
bedd9e8
delete logic changed to json by overloading
Dileepadari Jul 15, 2025
5c66af1
language strings and validation resolved
Dileepadari Jul 15, 2025
c2cede8
some linting validation
Dileepadari Jul 16, 2025
56d2258
transition mode removed and positions bug fixed
Dileepadari Jul 17, 2025
fae346b
bugs cleared for positions and undo, fixed dot warnings
Dileepadari Jul 17, 2025
9e5f2b3
minimap minimize logic added
Dileepadari Jul 18, 2025
b4dc905
permissions in backend implemented
Dileepadari Jul 21, 2025
d0c04ad
all permissions logic implemented and transition add by drag implemented
Dileepadari Jul 21, 2025
ab0128b
Merge remote-tracking branch 'Joomla/6.0-dev' into gsoc-workflow-dileep
bembelimen Jul 24, 2025
cd2eaed
changes done for review comments
Dileepadari Jul 24, 2025
900d128
Merge branch 'gsoc-workflow-dileep' of https://github.com/joomla-proj…
Dileepadari Jul 24, 2025
2288738
changes done for review comments:linting
Dileepadari Jul 24, 2025
48e024a
minor fix in css
Dileepadari Jul 25, 2025
5a533ca
js seperated to files
Dileepadari Jul 28, 2025
b63d859
small correction
Dileepadari Jul 28, 2025
16e1dc1
transitions dropdown added without keyboard support
Dileepadari Jul 29, 2025
a446fc0
implemented all the changes regarding dropdowns
Dileepadari Aug 3, 2025
2bb7f9c
deprecated functions removed
Dileepadari Aug 4, 2025
80cc021
accessibility improved
Dileepadari Aug 4, 2025
2897829
code quality increased in js
Dileepadari Aug 4, 2025
d8b3d26
Merge pull request #15 from joomla-projects/gsoc-workflow-experiment
Dileepadari Aug 7, 2025
bc7a18f
accessibility improved
Dileepadari Aug 12, 2025
9bd6574
edge and stage layout changed
Dileepadari Aug 13, 2025
245572d
accessibilityy fixer script added
Dileepadari Aug 13, 2025
8402df4
css added
Dileepadari Aug 15, 2025
0a56846
color contrast isues cleared
Dileepadari Aug 15, 2025
0adc955
Merge remote-tracking branch 'Joomla/6.0-dev' into gsoc25/gsoc-workfl…
bembelimen Aug 22, 2025
53d68c1
some changes for options dropdown and semantics
Dileepadari Aug 11, 2025
fc54e8e
changes for the keyboard navigation
Dileepadari Aug 27, 2025
73b0ae4
sql changes
Dileepadari Aug 27, 2025
9fe999a
css lint fixed
Dileepadari Aug 27, 2025
fcb3192
changed shortcuts
Dileepadari Aug 27, 2025
df147bb
removed unused dependency
Dileepadari Aug 28, 2025
f1af875
Added visual graph feature for non Admins in article at run transitio…
Dileepadari Aug 31, 2025
817771a
Implementation of graphical workflow editor feature
Dileepadari Aug 31, 2025
253d64a
Merge branch '6.1-dev' into gsoc-workflow
Dileepadari Aug 31, 2025
91c6786
removed redundant code
Dileepadari Aug 31, 2025
75fd79c
php style error cleared
Dileepadari Aug 31, 2025
eecbe26
Merge pull request #3 from Dileepadari/gsoc-workflow
Dileepadari Aug 31, 2025
f381723
cleared some of the issues from the feedback
Dileepadari Sep 3, 2025
836cebe
removed all redundant code and refactored
Dileepadari Sep 5, 2025
f8b6f66
some enhancements for language strings
Dileepadari Sep 5, 2025
da35661
some enhancements for language strings
Dileepadari Sep 5, 2025
e9c5711
some enhancements for language strings
Dileepadari Sep 5, 2025
0347162
some minor changes
Dileepadari Sep 5, 2025
c052a11
some minor changes
Dileepadari Sep 5, 2025
bf808aa
resolve singular plural issue in language strings
Dileepadari Sep 5, 2025
0ad971e
resolve singular plural issue in language strings
Dileepadari Sep 5, 2025
3c8076d
adjusted some issues
Dileepadari Sep 6, 2025
043c03d
adjusted some issues
Dileepadari Sep 6, 2025
ebcfc4e
cleared the issue of focus and blur events being triggered on clickin…
Dileepadari Sep 8, 2025
74d1cd2
cleared the issue with the multiple transitions
Dileepadari Sep 8, 2025
0580183
small change
Dileepadari Sep 10, 2025
f7970b9
Merge branch '6.1-dev' of github.com:Dileepadari/joomla-cms into 6.1-dev
Dileepadari Sep 27, 2025
f1f9d37
cleared issues in workflow modal in article
Dileepadari Oct 31, 2025
f10abcf
Merge branch '6.1-dev' into 6.1-dev
Dileepadari Oct 31, 2025
cd68c7b
cleared package issues
Dileepadari Oct 31, 2025
231e38b
Revert "cleared package issues"
Dileepadari Nov 3, 2025
a1c01b3
cleared package issues
Dileepadari Nov 3, 2025
ce379c4
Merge branch '6.1-dev' into 6.1-dev
Dileepadari Nov 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
--
-- Add position column to workflow stages table
--

ALTER TABLE `#__workflow_stages` ADD COLUMN `position` text NULL AFTER `default` /** CAN FAIL **/;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
--
-- Add position column to workflow stages table
--

ALTER TABLE "#__workflow_stages" ADD COLUMN "position" text NULL /** CAN FAIL **/;
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,8 @@ protected function preprocessForm(Form $form, $data, $group = 'content')

$this->workflowPreprocessForm($form, $data);

$form->setFieldAttribute('transition', 'layout', 'joomla.form.field.groupedlist-transition');

parent::preprocessForm($form, $data, $group);
}

Expand Down
26 changes: 26 additions & 0 deletions administrator/components/com_workflow/layouts/toolbar/redo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2025 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;

Factory::getApplication()->getDocument()->getWebAssetManager()
->useScript('webcomponent.toolbar-button');

$onclickAttr = 'onclick="WorkflowGraph.Event.fire(\'onClickRedoWorkflow\')"';
?>
<joomla-toolbar-button>
<button id="redo-workflow" class="btn btn-info action-button" <?php echo $onclickAttr ?>>
<span class="icon-redo icon-fw" aria-hidden="true"></span>
<?php echo Text::_('COM_WORKFLOW_REDO'); ?>
</button>
</joomla-toolbar-button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2025 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;

Factory::getApplication()->getDocument()->getWebAssetManager()
->useScript('webcomponent.toolbar-button');

$shortcutsPopupOptions = json_encode([
'src' => '#shortcuts-popup-content',
'width' => '800px',
'height' => 'fit-content',
'textHeader' => Text::_('COM_WORKFLOW_GRAPH_SHORTCUTS_TITLE'),
'preferredParent' => 'body',
]);
?>
<joomla-toolbar-button>
<button
class="btn btn-info action-button"
data-joomla-dialog="<?php echo htmlspecialchars($shortcutsPopupOptions, ENT_QUOTES, 'UTF-8'); ?>"
>
<span class="fa fa-keyboard" aria-hidden="true"></span>
<?php echo Text::_('COM_WORKFLOW_GRAPH_SHORTCUTS'); ?>
</button>
</joomla-toolbar-button>
26 changes: 26 additions & 0 deletions administrator/components/com_workflow/layouts/toolbar/undo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2025 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;

Factory::getApplication()->getDocument()->getWebAssetManager()
->useScript('webcomponent.toolbar-button');

$onclickAttr = 'onclick="WorkflowGraph.Event.fire(\'onClickUndoWorkflow\')"';
?>
<joomla-toolbar-button>
<button id="undo-workflow" class="btn btn-info action-button" <?php echo $onclickAttr ?>>
<span class="icon-undo-2 icon-fw" aria-hidden="true"></span>
<?php echo Text::_('COM_WORKFLOW_UNDO'); ?>
</button>
</joomla-toolbar-button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Simple Event Bus for cross-module communication
* Used to communicate between Joomla buttons and Vue app
*/
export default new class EventBus {
/**
* Internal registry of events
* @type {Object<string, Function[]>}
*/
constructor() {
this.events = {};
}

/**
* Trigger a custom event with optional payload
* @param {string} event - Event name
* @param {*} [data=null] - Optional payload
*/
fire(event, data = null) {
(this.events[event] || []).forEach((fn) => fn(data));
}

/**
* Register a callback for an event
* @param {string} event - Event name
* @param {Function} callback - Function to invoke on event
*/
listen(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}

/**
* Remove a listener from an event
* @param {string} event - Event name
* @param {Function} callback - Function to remove
*/
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter((fn) => fn !== callback);
}
}
}();
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import notifications from '../plugins/Notifications.es6.js';

/**
* Handles API communication for the workflow graph.
*/
class WorkflowGraphApi {
/**
* Initializes the WorkflowGraphApi instance.
*
* @throws {TypeError} If required options are missing.
*/
constructor() {
const {
apiBaseUrl,
extension,
} = Joomla.getOptions('com_workflow', {});

if (!apiBaseUrl || !extension) {
throw new TypeError(Joomla.Text._('COM_WORKFLOW_GRAPH_API_NOT_SET'));
}

this.baseUrl = apiBaseUrl;
this.extension = extension;
this.csrfToken = Joomla.getOptions('csrf.token', null);

if (!this.csrfToken) {
throw new TypeError(Joomla.Text._('COM_WORKFLOW_GRAPH_ERROR_CSRF_TOKEN_NOT_SET'));
}
}

/**
* Makes a request using Joomla.request.
*
* @param {string} url - The endpoint relative to baseUrl.
* @param {Object} [options={}] - Request config (method, data, headers).
* @returns {Promise<any>} The parsed response or error.
*/
async makeRequest(url, options = {}) {
const headers = options.headers || {};
headers['X-Requested-With'] = 'XMLHttpRequest';
options.headers = headers;
options[this.csrfToken] = 1;

return new Promise((resolve, reject) => {
Joomla.request({
url: `${this.baseUrl}${url}&extension=${this.extension}`,
...options,
onSuccess: (response) => {
const data = JSON.parse(response);
resolve(data);
},
onError: (xhr) => {
let message = 'COM_WORKFLOW_GRAPH_ERROR_UNKNOWN';
try {
const errorData = JSON.parse(xhr.responseText);
message = errorData.data || errorData.message || message;
} catch (e) {
message = xhr.statusText || message;
}
notifications.error(message);
reject(new Error(Joomla.Text._(message)));
},
});
});
}

/**
* Fetches workflow data by ID.
*
* @param {number} id - Workflow ID.
* @returns {Promise<Object|null>}
*/
async getWorkflow(id) {
return this.makeRequest(`&task=graph.getWorkflow&workflow_id=${id}&format=json`);
}

/**
* Fetches stages for a given workflow.
*
* @param {number} workflowId - Workflow ID.
* @returns {Promise<Object[]|null>}
*/
async getStages(workflowId) {
return this.makeRequest(`&task=graph.getStages&workflow_id=${workflowId}&format=json`);
}

/**
* Fetches transitions for a given workflow.
*
* @param {number} workflowId - Workflow ID.
* @returns {Promise<Object[]|null>}
*/
async getTransitions(workflowId) {
return this.makeRequest(`&task=graph.getTransitions&workflow_id=${workflowId}&format=json`);
}

/**
* Deletes a stage from a workflow.
*
* @param {number} id - Stage ID.
* @param {number} workflowId - Workflow ID.
* @param {boolean} [stageDelete=0] - Optional flag to indicate if the stage should be deleted or just trashed.
*
* @returns {Promise<boolean>}
*/
async deleteStage(id, workflowId, stageDelete = false) {
try {
const formData = new FormData();
formData.append('cid[]', id);
formData.append('workflow_id', workflowId);
formData.append('type', 'stage');
formData.append(this.csrfToken, '1');

const response = await this.makeRequest(`&task=${stageDelete ? 'graph.delete' : 'graph.trash'}&workflow_id=${workflowId}&format=json`, {
method: 'POST',
data: formData,
});

if (response && response.success) {
notifications.success(response?.data?.message || response?.message);
}
} catch (error) {
notifications.error(error.message);
throw error;
}
}

/**
* Deletes a transition from a workflow.
*
* @param {number} id - Transition ID.
* @param {number} workflowId - Workflow ID.
* @param {boolean} [transitionDelete=false] - Optional flag to indicate if the transition should be deleted or just trashed.
*
* @returns {Promise<boolean>}
*/
async deleteTransition(id, workflowId, transitionDelete = false) {
try {
const formData = new FormData();
formData.append('cid[]', id);
formData.append('workflow_id', workflowId);
formData.append('type', 'transition');
formData.append(this.csrfToken, '1');

const response = await this.makeRequest(`&task=${transitionDelete ? 'graph.delete' : 'graph.trash'}&workflow_id=${workflowId}&format=json`, {
method: 'POST',
data: formData,
});

if (response && response.success) {
notifications.success(response?.data?.message || response?.message);
}
} catch (error) {
notifications.error(error.message);
throw error;
}
}

/**
* Updates the position of a stage.
*
* @param {number} workflowId - Workflow ID.
* @param {Object} positions - Position objects {x, y} of updated stages.
* @returns {Promise<Object|null>}
*/
async updateStagePosition(workflowId, positions) {
try {
const formData = new FormData();
formData.append('workflow_id', workflowId);
formData.append(this.csrfToken, '1');

if (positions === null || Object.keys(positions).length === 0) {
return true;
}

Object.entries(positions).forEach(([id, position]) => {
formData.append(`positions[${id}][x]`, position.x);
formData.append(`positions[${id}][y]`, position.y);
});

const response = await this.makeRequest('&task=stages.updateStagesPosition&format=json', {
method: 'POST',
data: formData,
});

return !!(response && response.success);
} catch (error) {
notifications.error(error.message);
throw error;
}
}
}

export default new WorkflowGraphApi();
Loading
Loading