WFTK_EXPORT int wftk_task_subproc (void * session, XML * task, XML * subproc_datasheet) {}; WFTK_EXPORT int wftk_task_attach (void * session, XML * task, XML * datasheet) {}; |
wftk_task_retrieve
on it. The library then checks either the named datasheet or the
named task index, and fills in the task object with various details, which include the data objects which the
task either displays or requires. This task object may then be used to generate something to be presented to a user.
This function also includes a few shortcuts. For instance, if the "task object" is really a datasheet, then the currently
afforded potential task will be returned, if any. This is effectively a shortcut for creating a task object which refers to
the datasheet.
Note that the datasheet may not even contain the data items required for the task yet, if the requirements are determined
by the procdef.
WFTK_EXPORT XML * wftk_task_retrieve (void * session, XML * task) { XML * datasheet = NULL; XML * procdef = NULL; XML * mark; XML * data; WFTK_ADAPTOR * ad; const char *task_id; if (!task) return task; if (xml_is (task, "datasheet")) { datasheet = task; task = xml_create ("task"); xml_set (task, "process", xml_attrval (datasheet, "id")); xml_set (task, "dsrep", xml_attrval (datasheet, "repository")); } else { if (*xml_attrval (task, "process")) { datasheet = wftk_process_load (session, xml_attrval (task, "dsrep"), xml_attrval (task, "process")); xml_set (task, "process", xml_attrval (datasheet, "id")); xml_set (task, "dsrep", xml_attrval (datasheet, "repository")); } } if (!datasheet) { /* TODO: retrieval of non-datasheet tasks. (Needs doing in the ODBC adaptor, too.) */ /* ad = wftk_get_adaptor (session, TASKINDEX, NULL); mark = wftk_call_adaptor (ad, "get", task);*/ return 0; } procdef = _procdef_load (session, datasheet); task_id = xml_attrval (task, "id"); if (*task_id && *task_id != '!') { /* Explicit task, either ad-hoc or workflow. */ /* Let's look for an ad-hoc task with this ID. */ mark = xml_locf (datasheet, ".task[%s]", task_id); if (mark) { xml_set_nodup (task, "label", wftk_value_interpreta (session, datasheet, xml_attrval (mark, "label"))); xml_set (task, "status", "active"); xml_set (task, "role", xml_attrval (mark, "role")); xml_set (task, "user", xml_attrval (mark, "user")); xml_set_nodup (task, "loc", xml_getlocbuf (mark)); mark = xml_firstelem (mark); /* Copy data references over and that whole nine yards. */ while (mark) { if (xml_is (mark, "data")) { data = xml_create ("data"); xml_set (data, "id", xml_attrval (mark, "id")); xml_set (data, "type", xml_attrval (mark, "type")); xml_set (data, "value", wftk_value_get (session, datasheet, xml_attrval (data, "id"))); xml_set (data, "mode", *xml_attrval (data, "value")? "edit" : "input"); xml_append (task, data); } mark = xml_nextelem (mark); } if (!session) { if (procdef) xml_free (procdef); xml_free (datasheet); } return (task); /* TODO: proper cleanup. */ } /* Not found? Then on to the workflow status, and retrieve active tasks from the procdef. */ mark = xml_locf (datasheet, ".state.queue.item[%s]", xml_attrval (task, "id")); if (mark) { xml_set (task, "role", xml_attrval (mark, "role")); xml_set (task, "user", xml_attrval (mark, "user")); if (!strcmp (xml_attrval (mark, "where"), "datasheet")) { mark = xml_loc (datasheet, xml_attrval (mark, "loc")); } else { mark = xml_loc (procdef, xml_attrval (mark, "loc")); } } if (mark) { /* Mark is now the task definition, whether in procdef or ad-hoc in datasheet. */ xml_set_nodup (task, "label", wftk_value_interpreta (session, datasheet, xml_attrval (mark, "label"))); xml_set (task, "status", "active"); if (!*xml_attrval (task, "user")) { /* Get role/user from procdef if the datasheet ain't talkin. */ xml_set (task, "role", xml_attrval (mark, "role")); } if (!*xml_attrval (task, "user")) { xml_set (task, "user", xml_attrval (mark, "user")); } xml_set_nodup (task, "loc", xml_getlocbuf (mark)); mark = xml_firstelem (mark); /* Copy data references over and that whole nine yards. */ while (mark) { if (xml_is (mark, "data")) { data = xml_create ("data"); xml_set (data, "id", xml_attrval (mark, "id")); xml_set (data, "type", xml_attrval (mark, "type")); xml_set (data, "value", wftk_value_get (session, datasheet, xml_attrval (data, "id"))); xml_set (data, "mode", *xml_attrval (data, "value")? "edit" : "input"); xml_append (task, data); } mark = xml_nextelem (mark); } if (!session) { if (procdef) xml_free (procdef); xml_free (datasheet); } return (task); /* TODO: proper cleanup. */ } /* Still nothing. No task. */ xml_set (task, "status", "none"); } else if (procdef) { /* Potential task. */ /* If only one potential task exists, then we can return its data requirements. Otherwise? TODO: figure that out. */ /* Special case: if status="start" then we return top-level data from the procdef, unless that's overridden. */ if (!strcmp ("start", wftk_status_get (session, datasheet))) { xml_set (task, "status", "potential"); xml_set (task, "label", "Start process"); mark = xml_firstelem (procdef); while (mark) { if (xml_is (mark, "data")) { data = xml_create ("data"); xml_set (data, "id", xml_attrval (mark, "id")); xml_set (data, "type", xml_attrval (mark, "type")); xml_set (data, "value", wftk_value_get (session, datasheet, xml_attrval (data, "id"))); xml_set (data, "mode", *xml_attrval (data, "value")? "edit" : "input"); xml_append (task, data); } mark = xml_nextelem (mark); } return (task); /* TODO: proper cleanup. */ } /* TODO: Find other potential tasks, if any, and return the first which presents itself. */ /* No potential tasks available. */ xml_set (task, "status", "none"); } else { /* No procdef, no potential tasks. */ xml_set (task, "status", "none"); } /* TODO: if status="none" then check the enactment history for the named task. It might be complete already. */ return (task); } |
wftk_task_update
is also called
when assigning a user. So it's taking on a little more form.
WFTK_EXPORT int wftk_task_update (void * session, XML * task) { XML * datasheet = NULL; XML * mark; const char * task_id; int datachange = 0; int taskchange = 0; WFTK_ADAPTORLIST * adlist; if (!task) return 0; if (*xml_attrval (task, "process")) { datasheet = wftk_process_load (session, xml_attrval (task, "dsrep"), xml_attrval (task, "process")); } if (datasheet && xml_loc (task, "task.data")) { mark = xml_firstelem (task); while (mark) { if (xml_is (mark, "data")) { wftk_value_set (session, datasheet, xml_attrval (mark, "id"), xml_attrval (mark, "value")); datachange = 1; } mark = xml_nextelem (mark); } } task_id = xml_attrval (task, "id"); if (*task_id && *task_id != '!') { /* Explicit task, either ad-hoc or workflow. */ /* Let's look for an ad-hoc task with this ID. */ mark = xml_locf (datasheet, ".task[%s]", task_id); if (mark) { if (strcmp (xml_attrval (mark, "role"), xml_attrval (task, "role"))) { xml_set (mark, "role", xml_attrval (task, "role")); taskchange = 1; } if (strcmp (xml_attrval (mark, "user"), xml_attrval (task, "user"))) { xml_set (mark, "user", xml_attrval (task, "user")); wftk_user_retrieve (session, datasheet, xml_attrval (task, "user")); taskchange = 1; } } else { mark = xml_locf (datasheet, ".state.queue.item[%s]", xml_attrval (task, "id")); if (mark) { if (strcmp (xml_attrval (mark, "role"), xml_attrval (task, "role"))) { xml_set (mark, "role", xml_attrval (task, "role")); taskchange = 1; } if (strcmp (xml_attrval (mark, "user"), xml_attrval (task, "user"))) { xml_set (mark, "user", xml_attrval (task, "user")); taskchange = 1; } } } } if (taskchange) { /* Update the task indices. */ adlist = wftk_get_adaptorlist (session, TASKINDEX); wftk_call_adaptorlist (adlist, "taskput", task); wftk_free_adaptorlist (session, adlist); } if (datachange || taskchange) wftk_process_save (session, datasheet); return 1; } WFTK_EXPORT int wftk_task_complete (void * session, XML * task) { XML * datasheet = NULL; XML * procdef = NULL; XML * state; XML * queue; XML * mark; const char * task_id; int adhoc = 0; int complete = 1; WFTK_ADAPTORLIST * adlist = wftk_get_adaptorlist (session, TASKINDEX); if (!task) return 0; if (*xml_attrval (task, "process")) { datasheet = wftk_process_load (session, xml_attrval (task, "dsrep"), xml_attrval (task, "process")); procdef = _procdef_load (session, datasheet); } if (!datasheet) { wftk_call_adaptorlist (adlist, "taskcomplete", "", xml_attrval (task, "id")); wftk_free_adaptorlist (session, adlist); return 1; /* The task was ad-hoc with no process, so there's nothing left to do. */ } wftk_task_update (session, task); /* Log enactment of task. */ /* If task is ad-hoc, it needs to be deleted. */ task_id = xml_attrval (task, "id"); if (*xml_attrval (task, "id")) { mark = xml_locf (datasheet, ".task[%s]", xml_attrval (task, "id")); if (mark) { xml_delete (mark); adhoc = 1; wftk_call_adaptorlist (adlist, "taskcomplete", xml_attrval (task, "process"), xml_attrval (task, "id")); } } if (adhoc) { /* If the task is ad-hoc, we aren't going to do the queue thing below. */ /* Check for completion: no active ad-hoc tasks, nothing left on the queue. */ mark = xml_loc (datasheet, ".task"); if (mark) complete = 0; if (complete) { mark = xml_loc (datasheet, ".state.queue.item"); if (mark) complete = 0; } if (complete) wftk_status_set (session, datasheet, "complete"); /* Now save everything and clean up. */ wftk_enactment_write (session, datasheet, task, "action", "complete"); wftk_process_save (session, datasheet); if (!session) { if (procdef) xml_free (procdef); } if (datasheet) xml_free (procdef); wftk_free_adaptorlist (session, adlist); return 1; } /* The process is a regular workflow process, with procdef. Let's find (or create) the state queue. */ state = xml_loc (datasheet, ".state"); if (!state) { state = xml_create ("state"); xml_append (datasheet, state); } queue = xml_loc (datasheet, ".state.queue"); if (!queue) { queue = xml_create ("queue"); xml_append (state, queue); } if (*task_id && *task_id != '!') { /* Explicit task, either ad-hoc or workflow. */ mark = xml_locf (queue, "queue.item[%s]", task_id); if (mark) { /* Workflow task. */ xml_set (mark, "block", "resume"); wftk_call_adaptorlist (adlist, "taskcomplete", xml_attrval (task, "process"), xml_attrval (task, "id")); } else { /* Ad-hoc task. What has to happen? TODO: Answer this question, then do it. */ } } else { if (!strcmp ("start", wftk_status_get (session, datasheet))) { /* Special case: start state starts process. */ queue_procdef (session, datasheet, state, queue, procdef, "procdef"); wftk_status_set (session, datasheet, "active"); } else { /* TODO: Find potential task based on state, and complete that. */ } } process_procdef (session, datasheet, state, queue, procdef); /* Check for completion: no active ad-hoc tasks, nothing left on the queue. */ mark = xml_loc (datasheet, ".task"); if (mark) complete = 0; if (complete) { mark = xml_loc (datasheet, ".state.queue.item"); if (mark) complete = 0; } if (complete) wftk_status_set (session, datasheet, "complete"); wftk_enactment_write (session, datasheet, task, "action", "complete"); wftk_process_save (session, datasheet); wftk_free_adaptorlist (session, adlist); return 1; } |
WFTK_EXPORT int wftk_task_list (void * session, XML * list) { int count = 0; const char * state; const char * status; const char * userid; XML * datasheet = NULL; XML * procdef = NULL; XML * mark; XML * mark2; XML * hit; WFTK_ADAPTOR * ad; if (!list) return 0; if (*xml_attrval (list, "process")) { /* This is a process list. */ state = xml_attrval (list, "state"); if (!*state) state = "a"; userid = xml_attrval (list, "user"); datasheet = wftk_process_load (session, xml_attrval (list, "dsrep"), xml_attrval (list, "process")); if (!datasheet) { return 0; } if (strchr (state, 'a')) { /* Active tasks are included. */ procdef = _procdef_load (session, datasheet); /* Find ad-hoc tasks. */ mark = xml_firstelem (datasheet); while (mark) { if (xml_is (mark, "task") && (!*userid || !strcmp (userid, xml_attrval (mark, "user")))) { hit = xml_create ("task"); xml_set (hit, "id", xml_attrval (mark, "id")); xml_set_nodup (hit, "label", wftk_value_interpreta (session, datasheet, xml_attrval (mark, "label"))); xml_set (hit, "role", xml_attrval (mark, "role")); xml_set (hit, "user", xml_attrval (mark, "user")); xml_append (list, hit); } mark = xml_nextelem (mark); } /* Find active workflow tasks. */ mark = xml_loc (datasheet, ".state.queue"); if (mark) { mark = xml_firstelem (mark); while (mark) { if (!strcmp (xml_attrval (mark, "type"), "task")) { hit = xml_create ("task"); xml_set (hit, "id", xml_attrval (mark, "id")); xml_set (hit, "user", xml_attrval (mark, "user")); if (!strcmp (xml_attrval (mark, "where"), "datasheet")) { mark2 = xml_loc (datasheet, xml_attrval (mark, "loc")); } else { mark2 = xml_loc (procdef, xml_attrval (mark, "loc")); } if (mark2) { xml_set_nodup (hit, "label", wftk_value_interpreta (session, datasheet, xml_attrval (mark2, "label"))); xml_set (hit, "role", xml_attrval (mark2, "role")); } xml_append (list, hit); } mark = xml_nextelem (mark); } } /* Find potential tasks. No procdef means (per defn) no potential tasks. */ if (procdef) { status = wftk_status_get (session, datasheet); if (!strcmp (status, "start")) { /* Special case: start state. */ hit = xml_create ("task"); xml_set (hit, "id", "!active"); xml_set (hit, "label", "Start process"); xml_append (list, hit); } } } if (strchr (state, 'c')) { /* Closed tasks are included. */ /* TODO: Find enactment history matches. */ } /* TODO: Sort the list. */ } else { /* No process means we have to ask a task index. Fortunately this is *very easy*. */ ad = wftk_get_adaptor (session, TASKINDEX, NULL); count = 0; if (ad) { wftk_call_adaptor (ad, "tasklist", list); wftk_free_adaptor (session, ad); count = xml_attrvalnum (list, "count"); } } return count; } |
wftk_task_new
to create them, and
wftk_task_rescind
to delete them. They each use the same setup that task retrieval does (so that
a retrieved task can be reused in this way.)
WFTK_EXPORT int wftk_task_new (void * session, XML * task) { XML * datasheet = NULL; XML * newtask; XML * data; XML * newdata; WFTK_ADAPTORLIST * adlist; if (!task) return 0; if (*xml_attrval (task, "process")) { datasheet = wftk_process_load (session, xml_attrval (task, "dsrep"), xml_attrval (task, "process")); if (!datasheet) return 0; } if (datasheet && !*xml_attrval (task, "user") && *xml_attrval (task, "role")) { xml_set (task, "user", wftk_role_user (session, datasheet, xml_attrval (task, "role"))); } /* Inform task indices of new ad-hoc task. */ adlist = wftk_get_adaptorlist (session, TASKINDEX); wftk_call_adaptorlist (adlist, "tasknew", task); wftk_free_adaptorlist (session, adlist); if (!datasheet) return 1; /* The task was ad-hoc with no process, so there's nothing left to do. */ newtask = xml_create ("task"); xml_set (newtask, "id", xml_attrval (task, "id")); xml_set (newtask, "label", xml_attrval (task, "label")); xml_set (newtask, "role", xml_attrval (task, "role")); xml_set (newtask, "user", xml_attrval (task, "user")); data = xml_firstelem (task); while (data) { if (xml_is (data, "data")) { newdata = xml_create ("data"); xml_set (newdata, "id", xml_attrval (data, "id")); xml_set (newdata, "mode", xml_attrval (data, "mode")); xml_append (newtask, newdata); } data = xml_nextelem (data); } xml_append (datasheet, newtask); wftk_process_save (session, datasheet); return 1; } WFTK_EXPORT int wftk_task_rescind (void * session, XML * task) { XML * datasheet = NULL; XML * mark; WFTK_ADAPTORLIST * adlist; if (!task) return 0; if (*xml_attrval (task, "process")) { datasheet = wftk_process_load (session, xml_attrval (task, "dsrep"), xml_attrval (task, "process")); if (!datasheet) return 0; } /* Inform task indices of rescinded ad-hoc task. */ adlist = wftk_get_adaptorlist (session, TASKINDEX); wftk_call_adaptorlist (adlist, "taskdel", xml_attrval (task, "process"), xml_attrval (task, "id")); wftk_free_adaptorlist (session, adlist); if (!datasheet) return 1; /* The task was ad-hoc with no process, so there's nothing left to do. */ mark = xml_locf (datasheet, ".task[%s]", xml_attrval (task, "id")); if (mark) { xml_delete (mark); } wftk_enactment_write (session, datasheet, task, "action", "rescind"); wftk_process_save (session, datasheet); return 1; } |
WFTK_EXPORT int wftk_task_reject (void * session, XML * task) { XML * datasheet = NULL; XML * procdef = NULL; XML * mark; const char * task_id; int adhoc = 0; int complete = 1; WFTK_ADAPTORLIST * adlist = wftk_get_adaptorlist (session, TASKINDEX); if (!task) return 0; if (*xml_attrval (task, "process")) { datasheet = wftk_process_load (session, xml_attrval (task, "dsrep"), xml_attrval (task, "process")); procdef = _procdef_load (session, datasheet); } if (!datasheet) { wftk_call_adaptorlist (adlist, "taskreject", "", xml_attrval (task, "id")); wftk_free_adaptorlist (session, adlist); return 1; /* The task was ad-hoc with no process, so there's nothing left to do. */ } wftk_task_update (session, task); /* If task is ad-hoc, it needs to be marked as rejected (I'm not really comfortable with this.) TODO: think. */ task_id = xml_attrval (task, "id"); if (*xml_attrval (task, "id")) { mark = xml_locf (datasheet, ".task[%s]", xml_attrval (task, "id")); if (mark) { xml_set (mark, "status", "rejected"); adhoc = 1; wftk_call_adaptorlist (adlist, "taskreject", xml_attrval (task, "process"), xml_attrval (task, "id")); } } wftk_status_set (session, datasheet, "error"); /* Now save everything and clean up. */ wftk_enactment_write (session, datasheet, task, "action", "reject"); wftk_process_save (session, datasheet); if (!session) { if (procdef) xml_free (procdef); xml_free (datasheet); } wftk_free_adaptorlist (session, adlist); return 1; } |
This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |