mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Merge branch 'hooks' into 2.4.0
This commit is contained in:
commit
de34095eb5
11 changed files with 381 additions and 139 deletions
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
install (DIRECTORY bash fish vim zsh
|
||||
install (DIRECTORY bash fish vim zsh hooks
|
||||
DESTINATION ${TASK_DOCDIR}/scripts)
|
||||
install (DIRECTORY add-ons
|
||||
DESTINATION ${TASK_DOCDIR}/scripts
|
||||
|
|
23
scripts/hooks/on-add
Executable file
23
scripts/hooks/on-add
Executable file
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Input:
|
||||
# - New task JSON.
|
||||
read new_task
|
||||
|
||||
# Processing goes here.
|
||||
|
||||
# Output:
|
||||
# - Any line of JSON emitted is added as a new task.
|
||||
# - Any non-JSON emitted is displayed as a message.
|
||||
echo on-add
|
||||
|
||||
# Exit:
|
||||
# - 0 Means accept $new_task if JSON is not emitted.
|
||||
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||
# are additional tasks.
|
||||
# - 0 Means all non-JSON becomes footnote entries.
|
||||
# - 1 Means all non-JSON becomes error entries.
|
||||
# - 1 Means reject $new_task.
|
||||
exit 0
|
||||
|
22
scripts/hooks/on-exit
Executable file
22
scripts/hooks/on-exit
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Input:
|
||||
# - None
|
||||
|
||||
# Processing goes here.
|
||||
|
||||
# Output:
|
||||
# - Any line of JSON emitted is added as a new task.
|
||||
# - Any non-JSON emitted is displayed as a message.
|
||||
echo on-exit
|
||||
|
||||
# Exit:
|
||||
# - 0 Means accept $new_task if JSON is not emitted.
|
||||
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||
# are additional tasks.
|
||||
# - 0 Means all non-JSON becomes footnote entries.
|
||||
# - 1 Means all non-JSON becomes error entries.
|
||||
# - 1 Means reject $new_task.
|
||||
exit 0
|
||||
|
22
scripts/hooks/on-launch
Executable file
22
scripts/hooks/on-launch
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Input:
|
||||
# - None
|
||||
|
||||
# Processing goes here.
|
||||
|
||||
# Output:
|
||||
# - Any line of JSON emitted is added as a new task.
|
||||
# - Any non-JSON emitted is displayed as a message.
|
||||
echo on-launch
|
||||
|
||||
# Exit:
|
||||
# - 0 Means accept $new_task if JSON is not emitted.
|
||||
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||
# are additional tasks.
|
||||
# - 0 Means all non-JSON becomes footnote entries.
|
||||
# - 1 Means all non-JSON becomes error entries.
|
||||
# - 1 Means reject $new_task.
|
||||
exit 0
|
||||
|
25
scripts/hooks/on-modify
Executable file
25
scripts/hooks/on-modify
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Input:
|
||||
# - Original task JSON
|
||||
# - Modified task JSON
|
||||
read original_task
|
||||
read modified_task
|
||||
|
||||
# Processing goes here.
|
||||
|
||||
# Output:
|
||||
# - Any line of JSON emitted is added as a new task.
|
||||
# - Any non-JSON emitted is displayed as a message.
|
||||
echo on-modify
|
||||
|
||||
# Exit:
|
||||
# - 0 Means accept $new_task if JSON is not emitted.
|
||||
# - 0 Means accept all JSON as new tasks. If UUID matches $new_task, use this
|
||||
# JSON instead of $new_task. If UUID does not match $new_task, then these
|
||||
# are additional tasks.
|
||||
# - 0 Means all non-JSON becomes footnote entries.
|
||||
# - 1 Means all non-JSON becomes error entries.
|
||||
# - 1 Means reject $new_task.
|
||||
exit 0
|
||||
|
|
@ -566,6 +566,9 @@ void Config::createDefaultData (const std::string& data)
|
|||
throw std::string ("Error: rc.data.location does not exist - exiting according to rc.exit.on.missing.db setting.");
|
||||
|
||||
d.create ();
|
||||
|
||||
d += "hooks";
|
||||
d.create ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -221,10 +221,9 @@ int Context::initialize (int argc, const char** argv)
|
|||
// Initialize the database.
|
||||
tdb2.set_location (data_dir);
|
||||
|
||||
// Hook system init, plus post-start event occurring at the first possible
|
||||
// moment after hook initialization.
|
||||
// First opportunity to run a hook script.
|
||||
hooks.initialize ();
|
||||
hooks.trigger ("on-launch");
|
||||
hooks.onLaunch ();
|
||||
}
|
||||
|
||||
catch (const std::string& message)
|
||||
|
@ -233,6 +232,12 @@ int Context::initialize (int argc, const char** argv)
|
|||
rc = 2;
|
||||
}
|
||||
|
||||
catch (int)
|
||||
{
|
||||
// Hooks can terminate processing by throwing integers.
|
||||
rc = 4;
|
||||
}
|
||||
|
||||
catch (...)
|
||||
{
|
||||
error (STRING_UNKNOWN_ERROR);
|
||||
|
@ -297,6 +302,7 @@ int Context::run ()
|
|||
try
|
||||
{
|
||||
rc = dispatch (output);
|
||||
hooks.onExit ();
|
||||
|
||||
std::stringstream s;
|
||||
s << "Perf "
|
||||
|
@ -317,13 +323,15 @@ int Context::run ()
|
|||
<< " commit:" << timer_commit.total ()
|
||||
<< " sort:" << timer_sort.total ()
|
||||
<< " render:" << timer_render.total ()
|
||||
<< " hooks:" << timer_hooks.total ()
|
||||
<< " total:" << (timer_init.total () +
|
||||
timer_load.total () +
|
||||
timer_gc.total () +
|
||||
timer_filter.total () +
|
||||
timer_commit.total () +
|
||||
timer_sort.total () +
|
||||
timer_render.total ())
|
||||
timer_render.total () +
|
||||
timer_hooks.total ())
|
||||
<< "\n";
|
||||
debug (s.str ());
|
||||
}
|
||||
|
@ -385,7 +393,6 @@ int Context::run ()
|
|||
else
|
||||
std::cerr << *e << "\n";
|
||||
|
||||
hooks.trigger ("on-exit");
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ public:
|
|||
Timer timer_commit;
|
||||
Timer timer_sort;
|
||||
Timer timer_render;
|
||||
Timer timer_hooks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
353
src/Hooks.cpp
353
src/Hooks.cpp
|
@ -24,69 +24,19 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <iostream> // TODO Remove
|
||||
#include <cmake.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <Context.h>
|
||||
#include <Hooks.h>
|
||||
#include <Timer.h>
|
||||
#include <text.h>
|
||||
#include <i18n.h>
|
||||
|
||||
extern Context context;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Hook::Hook ()
|
||||
: _event ("")
|
||||
, _file ("")
|
||||
, _function ("")
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Hook::Hook (const std::string& e, const std::string& f, const std::string& fn)
|
||||
: _event (e)
|
||||
, _file (f)
|
||||
, _function (fn)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Hook::Hook (const Hook& other)
|
||||
{
|
||||
_event = other._event;
|
||||
_file = other._file;
|
||||
_function = other._function;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Hook& Hook::operator= (const Hook& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
_event = other._event;
|
||||
_file = other._file;
|
||||
_function = other._function;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Hooks::Hooks ()
|
||||
{
|
||||
// New 2.x hooks.
|
||||
_validTaskEvents.push_back ("on-task-add"); // Unimplemented
|
||||
_validTaskEvents.push_back ("on-task-modify"); // Unimplemented
|
||||
_validTaskEvents.push_back ("on-task-complete"); // Unimplemented
|
||||
_validTaskEvents.push_back ("on-task-delete"); // Unimplemented
|
||||
|
||||
_validProgramEvents.push_back ("on-launch");
|
||||
_validProgramEvents.push_back ("on-exit");
|
||||
_validProgramEvents.push_back ("on-file-read"); // Unimplemented
|
||||
_validProgramEvents.push_back ("on-file-write"); // Unimplemented
|
||||
_validProgramEvents.push_back ("on-synch"); // Unimplemented
|
||||
_validProgramEvents.push_back ("on-gc"); // Unimplemented
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -95,86 +45,285 @@ Hooks::~Hooks ()
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Enumerate all hooks, and tell API about the script files it must load in
|
||||
// order to call them. Note that API will perform a deferred read, which means
|
||||
// that if it isn't called, a script will not be loaded.
|
||||
void Hooks::initialize ()
|
||||
{
|
||||
// Allow a master switch to turn the whole thing off.
|
||||
bool big_red_switch = context.config.getBoolean ("extensions");
|
||||
if (big_red_switch)
|
||||
// Scan <rc.data.location>/hooks
|
||||
Directory d (context.config.get ("data.location"));
|
||||
d += "hooks";
|
||||
if (d.is_directory () &&
|
||||
d.readable ())
|
||||
{
|
||||
Config::const_iterator it;
|
||||
for (it = context.config.begin (); it != context.config.end (); ++it)
|
||||
{
|
||||
std::string type;
|
||||
std::string name;
|
||||
std::string value;
|
||||
_scripts = d.list ();
|
||||
std::sort (_scripts.begin (), _scripts.end ());
|
||||
}
|
||||
}
|
||||
|
||||
// "<type>.<name>"
|
||||
Nibbler n (it->first);
|
||||
if (n.getUntil ('.', type) &&
|
||||
type == "hook" &&
|
||||
n.skip ('.') &&
|
||||
n.getUntilEOS (name))
|
||||
{
|
||||
Nibbler n (it->second);
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Occurs when: On launch, after data structures are initiliazed, before
|
||||
// data is loaded.
|
||||
// Data fed to stdin: None
|
||||
// Exit code: 0: Success, proceed
|
||||
// !0: Failure, terminate
|
||||
// Output handled: 0: context.header ()
|
||||
// !0: context.error ()
|
||||
void Hooks::onLaunch ()
|
||||
{
|
||||
context.timer_hooks.start ();
|
||||
|
||||
// <path>:<function> [, ...]
|
||||
while (!n.depleted ())
|
||||
std::vector <std::string> matchingScripts = scripts ("on-launch");
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||
{
|
||||
std::string file;
|
||||
std::string function;
|
||||
if (n.getUntil (':', file) &&
|
||||
n.skip (':') &&
|
||||
n.getUntil (',', function))
|
||||
{
|
||||
context.debug (std::string ("Event '") + name + "' hooked by " + file + ", function " + function);
|
||||
Hook h (name, Path::expand (file), function);
|
||||
_all.push_back (h);
|
||||
std::string output;
|
||||
int status = execute (*i, "", output);
|
||||
|
||||
(void) n.skip (',');
|
||||
std::vector <std::string> lines;
|
||||
split (lines, output, '\n');
|
||||
std::vector <std::string>::iterator line;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
{
|
||||
if (line->length () && (*line)[0] == '{')
|
||||
{
|
||||
Task newTask (*line);
|
||||
context.tdb2.add (newTask);
|
||||
}
|
||||
else
|
||||
; // Was: throw std::string (format ("Malformed hook definition '{1}'.", it->first));
|
||||
}
|
||||
}
|
||||
context.header (*line);
|
||||
}
|
||||
}
|
||||
else
|
||||
context.debug ("Hooks::initialize --> off");
|
||||
{
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
context.error (*line);
|
||||
|
||||
throw 0; // This is how hooks silently terminate processing.
|
||||
}
|
||||
}
|
||||
|
||||
context.timer_hooks.stop ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Program hooks.
|
||||
bool Hooks::trigger (const std::string& event)
|
||||
// Occurs when: On exit, after processing is complete, before output is
|
||||
// displayed.
|
||||
// Data fed to stdin: None
|
||||
// Exit code: 0: Success
|
||||
// !0: Failure
|
||||
// Output handled: 0: context.footnote ()
|
||||
// !0: context.error ()
|
||||
void Hooks::onExit ()
|
||||
{
|
||||
return false;
|
||||
context.timer_hooks.start ();
|
||||
|
||||
std::vector <std::string> matchingScripts = scripts ("on-exit");
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||
{
|
||||
std::string output;
|
||||
int status = execute (*i, "", output);
|
||||
|
||||
std::vector <std::string> lines;
|
||||
split (lines, output, '\n');
|
||||
std::vector <std::string>::iterator line;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
{
|
||||
if (line->length () && (*line)[0] == '{')
|
||||
{
|
||||
Task newTask (*line);
|
||||
context.tdb2.add (newTask);
|
||||
}
|
||||
else
|
||||
context.footnote (*line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
context.error (*line);
|
||||
}
|
||||
}
|
||||
|
||||
context.timer_hooks.stop ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Task hooks.
|
||||
bool Hooks::trigger (const std::string& event, Task& task)
|
||||
// Occurs when: A task is created, before it is committed.
|
||||
// Data fed to stdin: task JSON
|
||||
// Exit code: 0: Success
|
||||
// !0: Failure
|
||||
// Output handled: 0: modified JSON
|
||||
// context.footnote ()
|
||||
// !0: context.error ()
|
||||
void Hooks::onAdd (Task& after)
|
||||
{
|
||||
return false;
|
||||
context.timer_hooks.start ();
|
||||
|
||||
std::vector <std::string> matchingScripts = scripts ("on-add");
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||
{
|
||||
std::string input = after.composeJSON () + "\n";
|
||||
std::string output;
|
||||
int status = execute (*i, input, output);
|
||||
|
||||
std::vector <std::string> lines;
|
||||
split (lines, output, '\n');
|
||||
std::vector <std::string>::iterator line;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
bool first = true;
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
{
|
||||
if (line->length () && (*line)[0] == '{')
|
||||
{
|
||||
Task newTask (*line);
|
||||
|
||||
if (first)
|
||||
{
|
||||
after = newTask;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
context.tdb2.add (newTask);
|
||||
}
|
||||
else
|
||||
context.footnote (*line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
context.error (*line);
|
||||
|
||||
throw 0; // This is how hooks silently terminate processing.
|
||||
}
|
||||
}
|
||||
|
||||
context.timer_hooks.stop ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Hooks::validProgramEvent (const std::string& event)
|
||||
// Occurs when: A task is modified, before it is committed.
|
||||
// Data fed to stdin: before JSON
|
||||
// after JSON
|
||||
// Exit code: 0: Success
|
||||
// !0: Failure
|
||||
// Output handled: 0: modified after JSON
|
||||
// context.footnote ()
|
||||
// !0: context.error ()
|
||||
void Hooks::onModify (const Task& before, Task& after)
|
||||
{
|
||||
if (std::find (_validProgramEvents.begin (), _validProgramEvents.end (), event) != _validProgramEvents.end ())
|
||||
return true;
|
||||
context.timer_hooks.start ();
|
||||
|
||||
return false;
|
||||
std::vector <std::string> matchingScripts = scripts ("on-modify");
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i)
|
||||
{
|
||||
std::string afterJSON = after.composeJSON ();
|
||||
std::string input = before.composeJSON ()
|
||||
+ "\n"
|
||||
+ afterJSON
|
||||
+ "\n";
|
||||
std::string output;
|
||||
int status = execute (*i, input, output);
|
||||
|
||||
std::vector <std::string> lines;
|
||||
split (lines, output, '\n');
|
||||
std::vector <std::string>::iterator line;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
bool first = true;
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
{
|
||||
if (line->length () && (*line)[0] == '{')
|
||||
{
|
||||
Task newTask (*line);
|
||||
|
||||
if (first)
|
||||
{
|
||||
after = newTask;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
context.tdb2.add (newTask);
|
||||
}
|
||||
else
|
||||
context.footnote (*line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (line = lines.begin (); line != lines.end (); ++line)
|
||||
context.error (*line);
|
||||
|
||||
throw 0; // This is how hooks silently terminate processing.
|
||||
}
|
||||
}
|
||||
|
||||
context.timer_hooks.stop ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Hooks::validTaskEvent (const std::string& event)
|
||||
std::vector <std::string> Hooks::scripts (const std::string& event)
|
||||
{
|
||||
if (std::find (_validTaskEvents.begin (), _validTaskEvents.end (), event) != _validTaskEvents.end ())
|
||||
return true;
|
||||
std::vector <std::string> matching;
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = _scripts.begin (); i != _scripts.end (); ++i)
|
||||
{
|
||||
if (i->find ("/" + event) != std::string::npos)
|
||||
{
|
||||
File script (*i);
|
||||
if (script.executable ())
|
||||
matching.push_back (*i);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return matching;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int Hooks::execute (
|
||||
const std::string& command,
|
||||
const std::string& input,
|
||||
std::string& output)
|
||||
{
|
||||
int status = -1;
|
||||
FILE* fp = popen (command.c_str (), "r+");
|
||||
if (fp)
|
||||
{
|
||||
// Write input to fp.
|
||||
if (input != "" &&
|
||||
input != "\n")
|
||||
{
|
||||
fputs (input.c_str (), fp);
|
||||
fflush (fp);
|
||||
}
|
||||
|
||||
// Read output from fp.
|
||||
output = "";
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
while (getline (&line, &len, fp) != -1)
|
||||
{
|
||||
output += line;
|
||||
free (line);
|
||||
line = NULL;
|
||||
}
|
||||
|
||||
fflush (fp);
|
||||
status = pclose (fp);
|
||||
context.debug (format ("Hooks::execute {1} (status {2})", command, status));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
31
src/Hooks.h
31
src/Hooks.h
|
@ -30,22 +30,6 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Hook class representing a single hook, which is just a three-way map.
|
||||
class Hook
|
||||
{
|
||||
public:
|
||||
Hook ();
|
||||
Hook (const std::string&, const std::string&, const std::string&);
|
||||
Hook (const Hook&);
|
||||
Hook& operator= (const Hook&);
|
||||
|
||||
public:
|
||||
std::string _event;
|
||||
std::string _file;
|
||||
std::string _function;
|
||||
};
|
||||
|
||||
// Hooks class for managing the loading and calling of hook functions.
|
||||
class Hooks
|
||||
{
|
||||
public:
|
||||
|
@ -56,18 +40,17 @@ public:
|
|||
|
||||
void initialize ();
|
||||
|
||||
bool trigger (const std::string&); // Program
|
||||
bool trigger (const std::string&, Task&); // Task
|
||||
void onLaunch ();
|
||||
void onExit ();
|
||||
void onAdd (Task&);
|
||||
void onModify (const Task&, Task&);
|
||||
|
||||
private:
|
||||
bool validProgramEvent (const std::string&);
|
||||
bool validTaskEvent (const std::string&);
|
||||
std::vector <std::string> scripts (const std::string&);
|
||||
int execute (const std::string&, const std::string&, std::string&);
|
||||
|
||||
private:
|
||||
std::vector <Hook> _all; // All current hooks.
|
||||
|
||||
std::vector <std::string> _validProgramEvents;
|
||||
std::vector <std::string> _validTaskEvents;
|
||||
std::vector <std::string> _scripts;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
17
src/TDB2.cpp
17
src/TDB2.cpp
|
@ -134,10 +134,13 @@ bool TF2::get (const std::string& uuid, Task& task)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TF2::add_task (const Task& task)
|
||||
{
|
||||
_tasks.push_back (task); // For subsequent queries
|
||||
_added_tasks.push_back (task); // For commit/synch
|
||||
Task hookTask (task);
|
||||
context.hooks.onAdd (hookTask);
|
||||
|
||||
/* TODO handle 'add' and 'log'.
|
||||
_tasks.push_back (hookTask); // For subsequent queries
|
||||
_added_tasks.push_back (hookTask); // For commit/synch
|
||||
|
||||
/* TODO handle 'add' and 'log'?
|
||||
int id = context.tdb2.next_id ();
|
||||
_I2U[id] = task.get ("uuid");
|
||||
_U2I[task.get ("uuid")] = id;
|
||||
|
@ -156,9 +159,13 @@ bool TF2::modify_task (const Task& task)
|
|||
{
|
||||
if (i->get ("uuid") == uuid)
|
||||
{
|
||||
*i = task;
|
||||
_modified_tasks.push_back (task);
|
||||
Task hookTask (task);
|
||||
context.hooks.onModify (*i, hookTask);
|
||||
|
||||
*i = hookTask;
|
||||
_modified_tasks.push_back (hookTask);
|
||||
_dirty = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue