void _repos_log (XML * repository, const char * action, const char * list_id, const char * key); void _repos_cleanup_object (XML * object); WFTK_EXPORT int repos_add (XML * repository, const char * list_id, XML * object) { WFTK_ADAPTOR * ad; XML * list; XML * index; XML * key_index; char * id; FILE * log; XML * mark; struct _repos_remote * sock = (struct _repos_remote *) xml_getbin (repository); if (sock) { /* Remote. */ xml_setf (sock->parms, "outgoing", "add %s -\n", list_id); _repos_send (sock); xml_set (sock->parms, "outgoing", xml_string (object)); _repos_send (sock); xml_set (sock->parms, "outgoing", "\n>>\n"); _repos_send (sock); if (*_repos_receive (sock) == '-') { return 1; } else { return 0; } } /* Find the list named. */ list = repos_defn (repository, list_id); if (!list && strcmp (list_id, "_publog")) return 1; if (strcmp (list_id, "_tasks") && strcmp (list_id, "_todo") && strcmp (list_id, "_tasks_really") && strcmp (list_id, "_taskindex")) xml_set (object, "list", list_id); /* Make sure this is properly marked, otherwise all kinds of code is going to get confused. */ /* Clean up special-meaning fields. */ _repos_cleanup_object (object); /* If we added this object earlier with no-workflow, then we use only-workflow to skip the actual add and pick up the workflow portion. */ if (!*xml_attrval (object, "only-workflow")) { /* Special field handling. */ repos_mark_time (repository, "now"); mark = xml_firstelem (list); while (mark) { if (xml_is (mark, "field")) { if (!strcmp (xml_attrval (mark, "special"), "add") || !strcmp (xml_attrval (mark, "special"), "now")) { xmlobj_set (object, list, xml_attrval (mark, "id"), xml_attrval (repository, "now")); } else if (!strcmp (xml_attrval (mark, "special"), "constant")) { xmlobj_set (object, list, xml_attrval (mark, "id"), xml_attrval (mark, "value")); } } mark = xml_nextelem (mark); } /* TODO: this is where xact functionality will check for approval workflow and such, and create a transaction if necessary. */ /* If the object is a _tasks object then we need to ensure that it has all the task fields it needs. */ if (!strcmp (list_id, "_tasks") || !strcmp (list_id, "_todo")) { if (!xmlobj_is_field (object, NULL, "key")) xmlobj_set (object, NULL, "key", ""); if (!xmlobj_is_field (object, NULL, "list")) xmlobj_set (object, NULL, "list", xml_attrval (object, "list")); if (!xmlobj_is_field (object, NULL, "obj")) xmlobj_set (object, NULL, "obj", xml_attrval (object, "process")); if (!xmlobj_is_field (object, NULL, "id")) xmlobj_set (object, NULL, "id", xml_attrval (object, "id")); if (!xmlobj_is_field (object, NULL, "state")) xmlobj_set (object, NULL, "state", "active"); if (!xmlobj_is_field (object, NULL, "label")) xmlobj_set (object, NULL, "label", xml_attrval (object, "label")); if (!xmlobj_is_field (object, NULL, "role")) xmlobj_set (object, NULL, "role", xml_attrval (object, "role")); if (!xmlobj_is_field (object, NULL, "user")) xmlobj_set (object, NULL, "user", xml_attrval (object, "user")); if (!xmlobj_is_field (object, NULL, "sched_start")) xmlobj_set (object, NULL, "sched_start", xml_attrval (object, "sched_start")); if (!xmlobj_is_field (object, NULL, "sched_end")) xmlobj_set (object, NULL, "sched_end", xml_attrval (object, "sched_end")); if (!xmlobj_is_field (object, NULL, "cost")) xmlobj_set (object, NULL, "cost", xml_attrval (object, "cost")); if (!xmlobj_is_field (object, NULL, "priority")) xmlobj_set (object, NULL, "priority", xml_attrval (object, "priority")); if (!xmlobj_is_field (object, NULL, "only_after")) xmlobj_set (object, NULL, "only_after", xml_attrval (object, "only_after")); if (!strcmp (list_id, "_todo")) { index = wftk_session_getuser (repository); if (index) xmlobj_set (object, NULL, "user", xml_attrval (index, "id")); } } /* If there is an index which is marked as having a special="key" field, then we let that index go first, to generate a key (this allows DBMS indexing of complex objects while the DB can make us guaranteed unique keys.) */ /* TODO: this entire operation suffers from a lack of transaction atomicity. */ key_index = xml_firstelem (list); while (key_index) { if (xml_is (key_index, "index")) { if (xml_search (key_index, "field", "special", "key")) { break; } } key_index = xml_nextelem (key_index); } if (key_index) { /* Whaddaya know, we found one. */ ad = wftk_get_adaptor (repository, LIST, xml_attrval (key_index, "storage")); if (ad) { xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "add", key_index, object); /* TODO: Check error -- here's where transaction atomicity will be implemented. */ wftk_free_adaptor (repository, ad); } } /* Now add the object to its main storage, possibly with a new key. Take special objects into account. */ if (!list) { if (!strcmp (list_id, "_publog")) { /* No defn to the contrary, we stash publish logs into a log file. */ log = _repos_fopen (repository, "_publog.log", "a"); fprintf (log, "%s ", xml_attrval (object, "start")); mark = xmlobj_field (object, NULL, "content"); fprintf (log, "%s\n", xml_attrval (mark, "value")); fprintf (log, "\n"); fclose (log); xml_free (object); return 0; } } ad = wftk_get_adaptor (repository, LIST, *xml_attrval (list, "id") ? xml_attrval (list, "id") : xml_attrval (list, "storage")); if (!ad) return 1; xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); repos_getkey (repository, list_id, object); wftk_call_adaptor (ad, "add", list, object); if (*xml_attrval (ad->parms, "error")) { wftk_free_adaptor (repository, ad); return 1; } if (!*xml_attrval (ad->parms, "error")) { repos_log (repository, 3, 1, NULL, "repmgr", "repos_add on %s[%s]", list_id, repos_getkey (repository, list_id, object)); if (strcmp (xml_attrval (list, "logging"), "off")) /* TODO: check overall setting as well. Need a setting checker which scans upward. */ _repos_log (repository, "add", list_id, repos_getkey (repository, list_id, object)); _repos_publish_obj (repository, list_id, object); } else { repos_log (repository, 1, 1, NULL, "repmgr", "error while adding %s[%]: %s", list_id, repos_getkey (repository, list_id, object), xml_attrval (ad->parms, "error")); } wftk_free_adaptor (repository, ad); /* Now we allow any existing non-key indices to write their own records. Whew. */ index = xml_firstelem (list); while (index) { if (xml_is (index, "index") && index != key_index) { ad = wftk_get_adaptor (repository, LIST, xml_attrval (index, "storage")); if (ad) { xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "add", index, object); /* TODO: Check error -- here's where transaction atomicity will be implemented. */ wftk_free_adaptor (repository, ad); } } index = xml_nextelem (index); } /* If this was a task object, then we explicitly write it through into the _taskindex list, as long as it doesn't have state="completed". */ if (!strcmp (list_id, "_tasks") || !strcmp (list_id, "_todo")) { xml_set_nodup (object, "state", xmlobj_get (object, NULL, "state")); if (strcmp (xml_attrval (object, "state"), "completed")) { index = xml_create ("record"); id = xmlobj_format (object, NULL, "[list]~[obj]~[id]"); xmlobj_fixkey (id); if (!strcmp (id, "~~")) { xmlobj_set_nodup (index, NULL, "id", xmlobj_get (object, NULL, "key")); free (id); } else { xmlobj_set_nodup (index, NULL, "id", id); } xmlobj_set_nodup (index, NULL, "key", xmlobj_get (object, NULL, "key")); xmlobj_set_nodup (index, NULL, "list", xmlobj_get (object, NULL, "list")); xmlobj_set_nodup (index, NULL, "obj", xmlobj_get (object, NULL, "obj")); xmlobj_set_nodup (index, NULL, "internal_id", xmlobj_get (object, NULL, "id")); xmlobj_set_nodup (index, NULL, "state", xmlobj_get (object, NULL, "state")); xmlobj_set_nodup (index, NULL, "label", xmlobj_get (object, NULL, "label")); xmlobj_set_nodup (index, NULL, "role", xmlobj_get (object, NULL, "role")); xmlobj_set_nodup (index, NULL, "user", xmlobj_get (object, NULL, "user")); xmlobj_set_nodup (index, NULL, "sched_start", xmlobj_get (object, NULL, "sched_start")); xmlobj_set_nodup (index, NULL, "sched_end", xmlobj_get (object, NULL, "sched_end")); xmlobj_set_nodup (index, NULL, "cost", xmlobj_get (object, NULL, "cost")); xmlobj_set_nodup (index, NULL, "priority", xmlobj_get (object, NULL, "priority")); xmlobj_set_nodup (index, NULL, "only_after", xmlobj_get (object, NULL, "only_after")); repos_add (ad->session, "_taskindex", index); xml_free (index); } } } /* If we're adding an object via (e.g.) repos_submit, then workflow will run later. */ if (!*xml_attrval (object, "no-workflow")) { /* To make sure that the object isn't saved multiple times by the workflow engine, we mark it as no-save. */ xml_set (object, "no-save", "yes"); /* WORKFLOW! Now that the object is added and comfy in its new home, let's see if any workflow should be started as a result. TODO: rollback using transactions in case workflow fails. */ /* If workflow was started, we have to store the (now changed) object -- but not via repos_mod, because this is still part of the add operation. */ if (repos_workflow_action_taken (repository, list, object, "add")) { repos_log (repository, 4, 1, NULL, "repmgr", "repos_add: saving after workflow start"); xml_unset (object, "no-save"); xml_unset (object, "repository"); /* TODO: Since this is identical to the code in repos_mod, we oughta combine the two into a utility function. */ ad = wftk_get_adaptor (repository, LIST, *xml_attrval (list, "id") ? xml_attrval (list, "id") : xml_attrval (list, "storage")); if (!ad) return 0; xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "update", list, object); wftk_free_adaptor (repository, ad); /* TODO: this entire operation suffers from a lack of transaction atomicity. */ index = xml_firstelem (list); while (index) { if (xml_is (index, "index")) { ad = wftk_get_adaptor (repository, LIST, *xml_attrval (list, "id") ? xml_attrval (list, "id") : xml_attrval (index, "storage")); if (ad) { xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "update", index, object); /* TODO: Check error -- here's where transaction atomicity will be implemented. */ wftk_free_adaptor (repository, ad); } } index = xml_nextelem (index); } } xml_unset (object, "no-save"); repos_log (repository, 4, 1, NULL, "repmgr", "repos_add: done with %s[%s]", list_id, repos_getkey (repository, list_id, object)); } else { repos_log (repository, 4, 1, NULL, "repmgr", "repos_add: done with %s[%s] (no workflow)", list_id, repos_getkey (repository, list_id, object)); } return 0; } |
void _repos_cleanup_object (XML * object) { XML * mark; XML * mark2; mark = xml_firstelem (object); mark2 = NULL; while (mark) { if (xml_is (mark, "field") && ( !strcmp (xml_attrval (mark, "id"), "_action") || !strcmp (xml_attrval (mark, "id"), "_control"))) { xml_delete_pretty (mark); if (mark2) mark = mark2; else mark = xml_firstelem (object); } else { mark2 = mark; mark = xml_nextelem (mark); } } } WFTK_EXPORT int repos_del (XML * repository, const char * list_id, const char * key) { WFTK_ADAPTOR * ad; XML * list; XML * index; struct _repos_remote * sock = (struct _repos_remote *) xml_getbin (repository); if (sock) { /* Remote. */ xml_setf (sock->parms, "outgoing", "del %s %s\n", list_id, key); _repos_send (sock); if (*_repos_receive (sock) == '-') { return 1; } else { return 0; } } /* Find the list named. */ list = repos_defn (repository, list_id); if (!list) return 0; ad = wftk_get_adaptor (repository, LIST, *xml_attrval (list, "id") ? xml_attrval (list, "id") : xml_attrval (list, "storage")); if (!ad) return 0; xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "delete", list, key); if (!*xml_attrval (ad->parms, "error")) { repos_log (repository, 3, 1, NULL, "repmgr", "repos_del: %s[%s]", list_id, key); if (strcmp (xml_attrval (list, "logging"), "off")) /* TODO: check overall setting as well. Need a setting checker which scans upward. */ _repos_log (repository, "del", list_id, key); _repos_publish_obj (repository, list_id, NULL); /* TODO: *not* the way to do this! */ } else { repos_log (repository, 1, 1, NULL, "repmgr", "error deleting %s[%s]", list_id, key); } wftk_free_adaptor (repository, ad); /* TODO: this entire operation suffers from a lack of transaction atomicity. */ index = xml_firstelem (list); while (index) { if (xml_is (index, "index")) { ad = wftk_get_adaptor (repository, LIST, xml_attrval (index, "storage")); if (ad) { xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "delete", index, key); /* TODO: Check error -- here's where transaction atomicity will be implemented. */ wftk_free_adaptor (repository, ad); } } index = xml_nextelem (index); } return 0; } |
static int _repos_write_task (XML * repository, XML * object, const char * key); WFTK_EXPORT int repos_mod (XML * repository, const char * list_id, XML * object, const char * key) { WFTK_ADAPTOR * ad; XML * list; XML * index; XML * mark; XML * field; XML * state; char * id; char * newstate; int error = 0; int object_local = 0; struct _repos_remote * sock = (struct _repos_remote *) xml_getbin (repository); if (!object && !key) return 1; /* This combination can occur in error situations but has no semantics. */ if (*xml_attrval (object, "no-save")) return 0; _repos_cleanup_object (object); if (sock) { /* Remote. */ if (key) { if (object) { xml_setf (sock->parms, "outgoing", "mod %s - %s\n", list_id, key); } else { xml_setf (sock->parms, "outgoing", "changed %s %s\n", list_id, key); } } else { xml_setf (sock->parms, "outgoing", "mod %s -\n", list_id); } _repos_send (sock); if (object) { xml_set (sock->parms, "outgoing", xml_string (object)); _repos_send (sock); xml_set (sock->parms, "outgoing", "\n>>\n"); _repos_send (sock); } if (*_repos_receive (sock) == '-') { return 1; } else { return 0; } } /* If this is a _tasks object, use the special function. */ if (!strcmp (list_id, "_tasks") || !strcmp (list_id, "_todo")) { return (_repos_write_task (repository, object, key)); } if (!strcmp (list_id, "_tasks_really")) list_id = "_tasks"; /* Find the list named. */ list = repos_defn (repository, list_id); if (!list) return 0; /* WORKFLOW! Workflow is handled using the memory-based object before it's stored. TODO: make sure this isn't stupid. TODO: rollback using transactions in case workflow fails? */ if (object) { /* 2005-11-12 - should this not work if there is no object? */ xml_set (object, "no-save", "yes"); repos_workflow_action_taken (repository, list, object, "mod"); xml_unset (object, "no-save"); } /* TODO: is there a safe way to detect state transitions in the non-object "changed" context? Probably not. */ /* But we *can* detect transition to an archive-to state, because no actual object should be in that state. */ if (object) { repos_mark_time (repository, "now"); mark = xml_firstelem (list); while (mark) { if (xml_is (mark, "field")) { if (!strcmp (xml_attrval (mark, "special"), "mod") || !strcmp (xml_attrval (mark, "special"), "now")) { xmlobj_set (object, list, xml_attrval (mark, "id"), xml_attrval (repository, "now")); } else if (!strcmp (xml_attrval (mark, "special"), "constant")) { xmlobj_set (object, list, xml_attrval (mark, "id"), xml_attrval (mark, "value")); } } mark = xml_nextelem (mark); } /* State transition? */ field = xml_search (list, "field", "type", "state"); if (!field) field = xml_search (object, "field", "id", "_state"); if (field) { xml_set_nodup (object, "newstate", xmlobj_get (object, list, xml_attrval (field, "id"))); if (strcmp (xml_attrval (object, "state"), xml_attrval (object, "newstate"))) { /* Transition! */ state = xml_search (list, "state", "id", xml_attrval (object, "newstate")); if (!state) { xmlobj_set (object, list, xml_attrval (field, "id"), xml_attrval (object, "state")); } else { if (*xml_attrval (state, "archive-to")) { /* The current object will be checking out today. */ /* TODO: As with most of this code, we need to handle error conditions competently. */ repos_del (repository, list_id, key); if (strcmp (xml_attrval (state, "archive-to"), "trash")) repos_add (repository, xml_attrval (state, "archive-to"), object); return 0; } else { /* TODO: this is where transition handling will happen. */ } } } xml_unset (object, "newstate"); } ad = wftk_get_adaptor (repository, LIST, *xml_attrval (list, "id") ? xml_attrval (list, "id") : xml_attrval (list, "storage")); if (!ad) return 0; xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); if (key) xml_set (object, "key", key); wftk_call_adaptor (ad, "update", list, object); if (*xml_attrval (ad->parms, "error")) { error = 1; } wftk_free_adaptor (repository, ad); /* TODO: this entire operation suffers from a lack of transaction atomicity. */ index = xml_firstelem (list); while (index) { if (xml_is (index, "index")) { ad = wftk_get_adaptor (repository, LIST, xml_attrval (index, "storage")); if (ad) { xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "update", index, object); /* TODO: Check error -- here's where transaction atomicity will be implemented. */ wftk_free_adaptor (repository, ad); } } index = xml_nextelem (index); } /* If this is a task object update, then we write the change through to _taskindex as well. */ if (!strcmp (list_id, "_tasks")) { id = xmlobj_format (object, NULL, "[list]~[obj]~[id]"); xmlobj_fixkey (id); if (!strcmp (id, "~~")) { free (id); id = xmlobj_get (object, NULL, "key"); } xml_set_nodup (object, "state", xmlobj_get (object, NULL, "state")); if (!strcmp (xml_attrval (object, "state"), "completed")) { repos_del (repository, "_taskindex", id); free (id); } else { mark = xml_copy (object); xml_set_nodup (mark, "key", id); xmlobj_set_nodup (mark, NULL, "internal_id", xmlobj_get (mark, NULL, "id")); xmlobj_set (mark, NULL, "id", xml_attrval (mark, "key")); repos_mod (repository, "_taskindex", mark, NULL); xml_free (mark); } } } else { /* Check for archive-to transition. */ object_local = 1; object = repos_get (repository, list_id, key); field = xml_search (list, "field", "type", "state"); if (field) { object = repos_get (repository, list_id, key); xml_set_nodup (object, "state", xmlobj_get (object, list, xml_attrval (field, "id"))); state = xml_search (list, "state", "id", xml_attrval (object, "state")); if (*xml_attrval (state, "archive-to")) { repos_del (repository, list_id, key); repos_add (repository, list_id, object); xml_free (object); return 0; } xml_free (object); } /* TODO: this entire operation suffers from a lack of transaction atomicity. */ index = xml_firstelem (list); while (index) { if (xml_is (index, "index")) { ad = wftk_get_adaptor (repository, LIST, xml_attrval (index, "storage")); if (ad) { xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); wftk_call_adaptor (ad, "update", index, object); /* TODO: Check error -- here's where transaction atomicity will be implemented. */ wftk_free_adaptor (repository, ad); } } index = xml_nextelem (index); } } if (!error) { repos_log (repository, 3, 1, NULL, "repmgr", "repos_mod on %s[%s]", list_id, repos_getkey (repository, list_id, object)); if (strcmp (xml_attrval (list, "logging"), "off")) { /* TODO: check overall setting as well. Need a setting checker which scans upward. */ _repos_log (repository, "mod", list_id, object ? repos_getkey (repository, list_id, object) : key); } _repos_publish_obj (repository, list_id, object); /* TODO: What if the key changed? */ } else { repos_log (repository, 1, 1, NULL, "repmgr", "error in repos_mod on %s[%s]: %d", list_id, repos_getkey (repository, list_id, object), error); } if (object_local) xml_free (object); return error; } WFTK_EXPORT int repos_merge (XML * repository, const char * list_id, XML * object, const char * key) { WFTK_ADAPTOR * ad; XML * list; XML * obj; XML * diff; XML * patch; XML * mark; int retval; struct _repos_remote * sock = (struct _repos_remote *) xml_getbin (repository); repos_log (repository, 3, 1, NULL, "repmgr", "repos_merge on %s[%s]", list_id, key); if (!object) return 1; /* Unlike mod, merge makes no sense in a key-only situation. */ if (sock) { /* Remote. */ if (key) { xml_setf (sock->parms, "outgoing", "merge %s - %s\n", list_id, key); } else { xml_setf (sock->parms, "outgoing", "merge %s -\n", list_id); } _repos_send (sock); if (object) { xml_set (sock->parms, "outgoing", xml_string (object)); _repos_send (sock); xml_set (sock->parms, "outgoing", "\n>>\n"); _repos_send (sock); } if (*_repos_receive (sock) == '-') { return 1; } else { return 0; } } /* If this is a _tasks object, use the special function. */ if (!strcmp (list_id, "_tasks") || !strcmp (list_id, "_todo")) { return (_repos_write_task (repository, object, key)); } /* Find the list named. */ list = repos_defn (repository, list_id); if (!list) return 0; repos_mark_time (repository, "now"); mark = xml_firstelem (list); while (mark) { if (xml_is (mark, "field")) { if (!strcmp (xml_attrval (mark, "special"), "mod") || !strcmp (xml_attrval (mark, "special"), "now")) { xmlobj_set (object, list, xml_attrval (mark, "id"), xml_attrval (repository, "now")); } else if (!strcmp (xml_attrval (mark, "special"), "constant")) { xmlobj_set (object, list, xml_attrval (mark, "id"), xml_attrval (mark, "value")); } } mark = xml_nextelem (mark); } ad = wftk_get_adaptor (repository, LIST, *xml_attrval (list, "id") ? xml_attrval (list, "id") : xml_attrval (list, "storage")); if (!ad) return 0; xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); /* Make sure we have a key and that the object correctly reflects it. */ if (!key) { key = (const char *) xmlobj_getkey (object, list); if (!key) return 0; xml_set_nodup (object, "key", (char *) key); key = xml_attrval (object, "key"); } else { xml_set (object, "key", key); } /* Retrieve the object's current value. */ obj = wftk_call_adaptor (ad, "get", list, key); wftk_free_adaptor (repository, ad); if (obj) { /* Perform a diff, then a patch, to do the merge. */ diff = xmlobj_diff (obj, list, object); patch = xmlobj_patch (obj, list, diff); xml_free (diff); xml_free (patch); /* Call repos_mod to do the honors of actually storing the object. */ /* TODO: consider how this interfaces with the whole transaction concept -- we probably don't want to throw out the diff. */ retval = repos_mod (repository, list_id, obj, key); /* Free things up. */ xml_free (obj); } else { repos_log (repository, 3, 1, NULL, "repmgr", "repos_merge: can't find %s[%s]; writing directly", list_id, key); retval = repos_mod (repository, list_id, object, key); /* Default to "mod" behavior if we can't find our object. */ } return (retval); } |
static int _repos_write_task (XML * repository, XML * object, const char * key) { XML * tasks_list; XML * repmgr_obj; XML * mark; XML * field; WFTK_ADAPTOR * ad; XML * wftk_obj; XML * ret; int repmgr_obj_changed = 0; int wftk_obj_changed = 0; int status_changed = 0; XML * diff; XML * patch; repos_log (repository, 4, 1, NULL, "repmgr", "_repos_write_task on _tasks[%s]", key); /* 1. Get repmgr task object for values in first and third categories. */ tasks_list = repos_defn (repository, "_tasks"); ad = wftk_get_adaptor (repository, LIST, "_tasks"); if (!ad) return 0; xml_set (ad->parms, "basedir", xml_attrval (repository, "basedir")); repmgr_obj = wftk_call_adaptor (ad, "get", tasks_list, key); wftk_free_adaptor (repository, ad); if (!repmgr_obj) return (repos_add (repository, "_tasks", object)); xml_set_nodup (repmgr_obj, "state", xmlobj_get (repmgr_obj, NULL, "state")); /* 1a. Make sure important system values aren't going to change or get dropped. */ xmlobj_set_nodup (object, NULL, "id", xmlobj_get (repmgr_obj, NULL, "id")); xmlobj_set_nodup (object, NULL, "list", xmlobj_get (repmgr_obj, NULL, "list")); xmlobj_set_nodup (object, NULL, "obj", xmlobj_get (repmgr_obj, NULL, "obj")); xmlobj_set_nodup (object, NULL, "key", xmlobj_get (repmgr_obj, NULL, "key")); /* 2. Ask wftk core for workflow-specific task information (values in the second category.) */ wftk_obj = xml_create ("task"); xml_set_nodup (repmgr_obj, "parent-list", xmlobj_get (repmgr_obj, NULL, "list")); xml_set_nodup (repmgr_obj, "parent-key", xmlobj_get (repmgr_obj, NULL, "obj")); xml_set_nodup (repmgr_obj, "id", xmlobj_get (repmgr_obj, NULL, "id")); if (*xml_attrval (repmgr_obj, "parent-key")) { xml_setf (wftk_obj, "dsrep", "list:%s", xml_attrval (repmgr_obj, "parent-list")); xml_set (wftk_obj, "process", xml_attrval (repmgr_obj, "parent-key")); xml_set (wftk_obj, "id", xml_attrval (repmgr_obj, "id")); wftk_task_retrieve (repository, wftk_obj); } /* 3. Has the status changed? */ field = xmlobj_is_field (object, NULL, "state"); if (field) { xml_set_nodup (object, "state", xmlobj_get_direct (field)); if (strcmp (xml_attrval (repmgr_obj, "state"), xml_attrval (object, "state"))) { status_changed = 1; } xml_delete_pretty (field); } /* 4. Find out whether changes are being made to the fields in the wftk task (i.e. fields pulled from the parent object). * Whether they are or not, these fields must also be removed from the incoming change object before we write the repmgr task object. * If a task-required field isn't given and the change is trying to set the task to complete, then we ignore the * state change at this juncture. ("Juncture" is a nice word, isn't it?) * TODO: perhaps other format specs than non-blank would be convenient here. */ mark = xml_firstelem (wftk_obj); while (mark) { if (xml_is (mark, "field")) { field = xmlobj_is_field (object, NULL, xml_attrval (mark, "id")); if (!field) { if (!strcmp (xml_attrval (mark, "required"), "yes")) status_changed = 0; } else { xml_set_nodup (object, "val1", xmlobj_get_direct (field)); if (!*xml_attrval (object, "val1") && !strcmp (xml_attrval (mark, "required"), "yes")) status_changed = 0; xml_set_nodup (object, "val2", xmlobj_get_direct (mark)); if (strcmp (xml_attrval (object, "val1"), xml_attrval (object, "val2"))) { xmlobj_set_direct (mark, xml_attrval (object, "val1")); wftk_obj_changed = 1; } xml_delete_pretty (field); } } mark = xml_nextelem (mark); } /* 5. The remaining fields and non-field XML in the change object are a diff (or replacement, we don't really care) * for the repmgr raw task object. Since we already have retrieved the repmgr obj, we don't want to call repos_merge, as * that would simply retrieve it again. Instead, we do the merge here, and call repos_mod on _tasks_really in order to * save and index it, and do any state-determined archival. (We need to reinsert the state field, of course.) */ if (status_changed) xmlobj_set (object, NULL, "state", xml_attrval (object, "state")); diff = xmlobj_diff (repmgr_obj, tasks_list, object); xml_unset (diff, "state"); /* TODO: maybe remove other attributes as well? */ patch = xmlobj_patch (repmgr_obj, tasks_list, diff); mark = xml_firstelem (patch); if (mark) repmgr_obj_changed = 1; xml_free (diff); xml_free (patch); if (repmgr_obj_changed) { repos_mod (repository, "_tasks_really", repmgr_obj, NULL); } /* TODO: same musing as in repos_merge as to whether we want to throw away the diff we just made. */ /* 6. If the state changed, and the new state is "completed", then we call wftk_task_complete, which will also update any data * appropriate from the parent object. Otherwise, we simply call wftk_task_update. */ if (status_changed && !strcmp (xml_attrval (object, "state"), "completed")) { wftk_task_complete (repository, wftk_obj); } else if (wftk_obj_changed) { wftk_task_update (repository, wftk_obj); } return (0); } |
WFTK_EXPORT const char * repos_getkey (XML * repository, const char * list_id, XML * object) { const char * key; XML * defn; /* See if we've cached a key value. */ key = xml_attrval (object, "key"); if (*key) return (key); /* Else create a new key. */ defn = repos_defn (repository, list_id); key = (const char *) xmlobj_getkey (object, defn); if (key) xml_set_nodup (object, "key", (char *) key); return xml_attrval (object, "key"); } |
void _repos_log (XML * repository, const char * action, const char * list_id, const char * key) { FILE * log; XML * scratch = xml_create ("s"); struct tm * timeptr; time_t julian; xml_setf (scratch, "f", "_log/%s.txt", list_id); log = _repos_fopen (repository, xml_attrval (scratch, "f"), "a"); if (log) { time (&julian); timeptr = localtime (&julian); fprintf (log, "%s\t%04d-%02d-%02d %02d:%02d:%02d\t\t%s\n", /* TODO: include user information! */ action, timeptr->tm_year + 1900, timeptr->tm_mon + 1, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, key); fclose (log); } xml_free (scratch); } |
This code and documentation are released under the terms of the GNU license. They are copyright (c) 2001-2005, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |