|
WFTK_EXPORT int wftk_status_set (XML * session, XML * datasheet, const char * status) {
_status_set (session, datasheet, status, 1);
}
static int _status_set (XML * session, XML * datasheet, const char * status, int invoke_before) {
const char * cur_status;
WFTK_ADAPTORLIST * adlist;
XML * workflow;
XML * procdef;
XML * state;
XML * task;
XML * oldstate = NULL;
XML * newstate = NULL;
XML * transition = NULL;
if (!datasheet) return 0;
cur_status = wftk_status_get (session, datasheet);
if (!strcmp (cur_status, status)) return 1;
/* Find any known state descriptions for the current status and the new status. */
workflow = xml_firstelem (datasheet);
while (workflow) {
if (*xml_attrval (workflow, "state")) {
procdef = _procdef_load (session, workflow);
state = xml_firstelem (procdef);
while (state) {
if (xml_is (state, "state")) {
if (!oldstate && !strcmp (xml_attrval (state, "id"), cur_status)) {
oldstate = state;
xml_set (oldstate, "workflow-id", xml_attrval (workflow, "id"));
}
if (!newstate && !strcmp (xml_attrval (state, "id"), status)) {
newstate = state;
xml_set (newstate, "workflow-id", xml_attrval (workflow, "id"));
}
if (oldstate && newstate) break;
}
state = xml_nextelem (state);
}
}
if (oldstate && newstate) break;
workflow = xml_nextelem (workflow);
}
/* If an oldstate was found, look for a transition which describes our current one. */
transition = xml_locf (oldstate, ".to[%s]", status);
if (transition) xml_set (transition, "workflow-id", xml_attrval (oldstate, "workflow-id"));
/* If we have a "before" procdef, and if there is no item marked as being the ,
activate the "before" procdef and suspend. */
if (invoke_before && transition && *xml_attrval (transition, "before")) {
wftk_process_start (session, datasheet, NULL, xml_attrval (transition, "before"));
return 1;
}
/* Get a list of taskindex adaptors for notification, because we'll be doing lots of task/process stuff now. */
adlist = wftk_get_adaptorlist (session, TASKINDEX);
/* We're not suspended; thus we're actually changing the state. */
/* Search for all active transition tasks and rescind them. Transition tasks start with '!'. */
while (task = xml_firstelem (datasheet)) {
while (task) {
if (xml_is (task, "task")) {
if (*xml_attrval (task, "id") == '!') {
wftk_call_adaptorlist (adlist, "taskdel", xml_attrval (datasheet, "id"), xml_attrval (task, "id"));
xml_delete (task);
break;
}
}
task = xml_nextelem (task);
}
if (!task) break;
}
/* Now, change the state. */
xml_set (datasheet, "status", status);
wftk_process_save (session, datasheet);
/* Notify task indices. */
if (!strcmp (status, "complete")) {
wftk_call_adaptorlist (adlist, "proccomplete", xml_attrval (datasheet, "id"));
} else if (!strcmp (status, "error")) {
wftk_call_adaptorlist (adlist, "procerror", xml_attrval (datasheet, "id"));
} else {
wftk_call_adaptorlist (adlist, "procput", datasheet);
}
/* If the new state has any "on" elements, we start all named procdefs. */
/* If the new state has any "to" elements with mode="task" then we start all such tasks. */
transition = xml_firstelem (newstate);
while (transition) {
if (xml_is (transition, "on")) {
/* TODO: implement this, both for linked procdefs and for queueing explicit workflow in the "on" tag. */
if (*xml_attrval (transition, "procdef")) {
wftk_process_start (session, datasheet, xml_attrval (transition, "pdrep"), xml_attrval (transition, "procdef"));
} else {
queue_procdef (session, datasheet, transition, ".workflow[%d]", xml_attrvalnum (newstate, "workflow-id"));
process_procdef (session, datasheet, xml_loc (datasheet, ".state"), xml_loc (datasheet, ".state.queue"));
}
} else if (xml_is (transition, "to")) {
if (!strcmp (xml_attrval (transition, "mode"), "task")) {
task = xml_create ("task");
xml_copyinto (task, transition); /* Grab any data, user, role, label, etc. */
xml_setf (task, "id", "!%s", xml_attrval (transition, "id"));
xml_append_pretty (datasheet, task);
wftk_call_adaptorlist (adlist, "tasknew", task);
}
}
transition = xml_nextelem (transition);
}
wftk_free_adaptorlist (session, adlist);
if (!strcmp (status, "complete")) {
/* TODO: If the process is attached as a subproc of another task, retrieve that task and complete it. */
}
return 1;
}
|