Как сохранить историю изменения параметров задачи
В TrackStudio начиная с версии 4.0.9 есть встроенный механизм аудита изменений. Для того, чтобы его задействовать, нужно в интересующем вас процессе создать операцию с названием *. После этого все изменения, происходящие с задачей, будут записываться с помощью этой операции, причем без учета прав пользователя (если пользователь может редактировать задачу, либо выполнять любую операцию с ней - системное сообщение также будет создаваться). Вы можете настроить права на просмотр этой операции, точно так же, как и для остальных операций.
В TrackStudio версий с 4.0 по 4.0.8 встроенной возможности нет, но такую функциональность можно организовать с помощью триггеров. Это решение можно рассматривать также просто как пример использования триггеров.
Свойства задачи могут измениться как при редактировании самой задачи, так и при выполенении операции. Значит нам понадобится не один, а два триггера разных типов, работающих по одному принципу. Т.к. для отслеживания изменений нам нужно знать состояние задачи до внесения этих изменений, триггер типа After не подойдет - тогда изменения уже записаны. Триггер типа Before нам не подойдет потому, что запись об изменениях произойдет раньше самих изменений, что, во-первых, может снова изменить свойства задачи, во-вторых, может отразить неверную информацию, если само изменение не состоялось из-за ошибки.
Следовательно нам нужны два триггера типа Instead Of.
Для редактирования задачи напишем триггер Instead Of Edit Task. Он должен соответствовать интерфейсу com.trackstudio.external.TaskTrigger и располагаться в папке
package scripts.instead_of_edit_task;
import com.trackstudio.app.adapter.AdapterManager;
import com.trackstudio.app.csv.CSVImport;
import com.trackstudio.exception.GranException;
import com.trackstudio.external.TaskTrigger;
import com.trackstudio.secured.*;
import com.trackstudio.startup.I18n;
import java.util.Calendar;
/**
* Скрипт сохраняет все изменения задачи
*/
public class LogChanges implements TaskTrigger {
public SecuredTaskTriggerBean execute(SecuredTaskTriggerBean task) throws GranException {
StringBuffer sb2 = new StringBuffer();
StringBuffer sb = new StringBuffer();
sb2.append("<table class=\"general\" cellpadding=4>");
String budget = task.getBudgetAsString();
Calendar deadline = task.getDeadline();
SecuredPrstatusBean group = task.getHandlerGroup();
SecuredUserBean user = task.getHandlerUser();
SecuredPriorityBean priority = task.getPriority();
SecuredResolutionBean resolution = task.getResolution();
String description = task.getDescription();
String name = task.getName();
String alias = task.getShortname();
SecuredTaskBean oldTask = new SecuredTaskBean(task.getId(), task.getSecure());
String _name = oldTask.getName();
String _alias = oldTask.getShortname();
String _budget = oldTask.getBudgetAsString();
Calendar _deadline = oldTask.getDeadline();
SecuredPrstatusBean _group = oldTask.getHandlerGroup();
SecuredUserBean _user = oldTask.getHandlerUser();
SecuredPriorityBean _priority = oldTask.getPriority();
SecuredResolutionBean _resolution = oldTask.getResolution();
String _description = oldTask.getDescription();
if ((_name != null && !_name.equals(name)) || (_name == null && name != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "NAME"));
sb.append("</th>");
sb.append("<td><strike>");
sb.append(_name);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(name);
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_alias != null && !_alias.equals(alias)) || (_alias == null && alias != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "ALIAS"));
sb.append("</th>");
sb.append("<td><strike>");
sb.append(_alias);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(alias);
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_budget != null && !_budget.equals(budget)) || (_budget == null && budget != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "BUDGET"));
sb.append("</th>");
sb.append("<td><strike>");
sb.append(_budget);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(budget);
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_deadline != null && !_deadline.equals(deadline)) || (_deadline == null && deadline != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "DEADLINE"));
sb.append("</th>");
sb.append("<td><strike>");
if (_deadline != null)
sb.append(task.getSecure().getUser().getDateFormatter().parse(_deadline));
sb.append("</strike></td>");
sb.append("<td>");
if (deadline != null)
sb.append(task.getSecure().getUser().getDateFormatter().parse(deadline));
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_priority != null && !_priority.equals(priority)) || (_priority == null && priority != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "PRIORITY"));
sb.append("</th>");
sb.append("<td><strike>");
if (_priority != null)
sb.append(_priority.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (priority != null)
sb.append(priority.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_resolution != null && !_resolution.equals(resolution)) || (_resolution == null && resolution != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "RESOLUTION"));
sb.append("</th>");
sb.append("<td><strike>");
if (_resolution != null)
sb.append(_resolution.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (resolution != null)
sb.append(resolution.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_user != null && !_user.equals(user)) || (_user == null && user != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "HANDLER"));
sb.append("</th>");
sb.append("<td><strike>");
if (_user != null)
sb.append(_user.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (user != null)
sb.append(user.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_group != null && !_group.equals(group)) || (_group == null && group != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "HANDLER"));
sb.append("</th>");
sb.append("<td><strike>");
if (_group != null)
sb.append(_group.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (group != null)
sb.append(group.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_description != null && !_description.equals(description)) || (_description == null && description != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(task.getSecure(), "DESCRIPTION"));
sb.append("</th>");
sb.append("<td><strike>");
sb.append(_description);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(description);
sb.append("</td>");
sb.append("</tr>\n");
}
if (task.getUdfValues() != null && !task.getUdfValues().isEmpty()) {
for (Object okey : task.getUdfValues().keySet()) {
String key = okey.toString();
String value = task.getUdfValues().get(key).toString();
String oldValue = AdapterManager.getInstance().getSecuredUDFAdapterManager()
.getTaskUDFValue(task.getSecure(), task.getId(), key);
if ((oldValue != null && !oldValue.equals(value))
|| (oldValue == null && value != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(key);
sb.append("</th>");
sb.append("<td><strike>");
sb.append(oldValue);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(value);
sb.append("</td>");
sb.append("</tr>\n");
}
}
}
SecuredMessageTriggerBean createMessage = null;
if (sb.length() > 0) {
sb2.append(sb);
sb2.append("</table>\n");
String mstatusId = CSVImport.findMessageTypeIdByName("Log", task.getCategory().getName());
/**
* Создаем SecuredMessageTriggerBean
*/
createMessage = new SecuredMessageTriggerBean(
null /* идентификатор */,
sb2.toString() /* текст комментария */,
Calendar.getInstance() /* время выполнения операции */,
null /* потраченное время */,
task.getDeadline() /* Сроки выполнения задачи (deadline) */,
task.getBudget() /* бюджет */,
task.getId() /* задача */,
task.getSecure().getUserId() /* автор операции */,
null /* резолюция */,
task.getPriorityId() /* приоритет */,
task.getHandlerId() /* ответственные */,
task.getHandlerUserId() /* ответственный */,
task.getHandlerGroupId() /* ответственный, если нужно задать группу в качестве ответственного */,
mstatusId /* тип операции */,
null /* Map с дополнительными полями */,
task.getSecure() /* SessionContext */,
null /* вложения */);
/**
* выполняем
*/
}
task.update(true);
if (createMessage != null) createMessage.create(false);
return task;
}
}
Этот триггер нужно подключить в настройках категорий тех задач, изменения в которых требуется отслеживать. При этом в процессах задач должна быть операция "Log", которая не изменяет состояние задачи. Название операции можно, конечно, поменять. Не забудьте тогда сменить его и в обоих триггерах.
Далее, для того, чтобы отслеживать изменения в задачах, произведенные с помощью операций (добавилении сообщений), нам понадобится триггер Instead Of Add Message. Он должен соответствовать интерфейсу com.trackstudio.external.OperationTrigger и располагаться в папке
package scripts.instead_of_add_message;
import com.trackstudio.app.adapter.AdapterManager;
import com.trackstudio.app.csv.CSVImport;
import com.trackstudio.exception.GranException;
import com.trackstudio.external.OperationTrigger;
import com.trackstudio.secured.*;
import com.trackstudio.startup.I18n;
import java.util.Calendar;
public class LogChanges implements OperationTrigger {
public SecuredMessageTriggerBean execute(SecuredMessageTriggerBean message) throws GranException {
StringBuffer sb2 = new StringBuffer();
StringBuffer sb = new StringBuffer();
sb2.append("<table class=\"general\" cellpadding=4>");
String budget = message.getBudgetAsString();
Calendar deadline = message.getDeadline();
SecuredPrstatusBean group = message.getHandlerGroup();
SecuredUserBean user = message.getHandlerUser();
SecuredPriorityBean priority = message.getPriority();
SecuredResolutionBean resolution = message.getResolution();
SecuredStatusBean state = message.getTask().getStatus();
String _budget = message.getTask().getBudgetAsString();
Calendar _deadline = message.getTask().getDeadline();
SecuredPrstatusBean _group = message.getTask().getHandlerGroup();
SecuredUserBean _user = message.getTask().getHandlerUser();
SecuredPriorityBean _priority = message.getTask().getPriority();
SecuredResolutionBean _resolution = message.getTask().getResolution();
SecuredStatusBean _state = null;
for (SecuredTransitionBean t : message.getMstatus().getTransitions()) {
if (t.getStart().equals(state)) _state = t.getFinish();
break;
}
if ((_budget != null && !_budget.equals(budget)) || (_budget == null && budget != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "BUDGET"));
sb.append("</th>");
sb.append("<td><strike>");
sb.append(_budget);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(budget);
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_deadline != null && !_deadline.equals(deadline)) || (_deadline == null && deadline != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "DEADLINE"));
sb.append("</th>");
sb.append("<td><strike>");
if (_deadline != null)
sb.append(message.getSecure().getUser().getDateFormatter().parse(_deadline));
sb.append("</strike></td>");
sb.append("<td>");
if (deadline != null)
sb.append(message.getSecure().getUser().getDateFormatter().parse(deadline));
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_priority != null && !_priority.equals(priority)) || (_priority == null && priority != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "PRIORITY"));
sb.append("</th>");
sb.append("<td><strike>");
if (_priority != null)
sb.append(_priority.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (priority != null)
sb.append(priority.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_resolution != null && !_resolution.equals(resolution)) || (_resolution == null && resolution != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "RESOLUTION"));
sb.append("</th>");
sb.append("<td><strike>");
if (_resolution != null)
sb.append(_resolution.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (resolution != null)
sb.append(resolution.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_user != null && !_user.equals(user)) || (_user == null && user != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "HANDLER"));
sb.append("</th>");
sb.append("<td><strike>");
if (_user != null)
sb.append(_user.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (user != null)
sb.append(user.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_group != null && !_group.equals(group)) || (_group == null && group != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "HANDLER"));
sb.append("</th>");
sb.append("<td><strike>");
if (_group != null)
sb.append(_group.getName());
sb.append("</strike></td>");
sb.append("<td>");
if (group != null)
sb.append(group.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if ((_state != null && !_state.equals(state)) || (_state == null && state != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(I18n.getString(message.getSecure(), "TASK_STATE"));
sb.append("</th>");
sb.append("<td><strike>");
sb.append(_state.getName());
sb.append("</strike></td>");
sb.append("<td>");
sb.append(state.getName());
sb.append("</td>");
sb.append("</tr>\n");
}
if (message.getUdfValues() != null && !message.getUdfValues().isEmpty()) {
for (Object okey : message.getUdfValues().keySet()) {
String key = okey.toString();
String value = message.getUdfValues().get(key).toString();
String oldValue = AdapterManager.getInstance().getSecuredUDFAdapterManager()
.getTaskUDFValue(message.getSecure(), message.getTaskId(), key);
if ((oldValue != null && !oldValue.equals(value))
|| (oldValue == null && value != null)) {
sb.append("<tr>");
sb.append("<th align=\"right\">");
sb.append(key);
sb.append("</th>");
sb.append("<td><strike>");
sb.append(oldValue);
sb.append("</strike></td>");
sb.append("<td>");
sb.append(value);
sb.append("</td>");
sb.append("</tr>\n");
}
}
}
SecuredMessageTriggerBean createMessage = null;
if (sb.length() > 0) {
sb2.append(sb);
sb2.append("</table>\n");
String mstatusId = CSVImport.findMessageTypeIdByName("Log", message.getTask().getCategory().getName());
/**
* Создаем SecuredMessageTriggerBean
*/
createMessage = new SecuredMessageTriggerBean(
null /* индентификатор */,
sb2.toString() /* текст комментария */,
Calendar.getInstance() /* время выполнения операции */,
null /* потраченное время */,
message.getDeadline() /* Сроки выполнения задачи (deadline) */,
message.getBudget() /* бюджет */,
message.getTaskId() /* задача */,
message.getSecure().getUserId() /* автор операции */,
null /* резолюция */,
message.getPriorityId() /* приоритет */,
message.getHandlerId() /* ответственные */,
message.getHandlerUserId() /* ответственный */,
message.getHandlerGroupId() /* ответственный, если нужно задать группу в качестве ответственного */,
mstatusId /* тип операции */,
null /* Map с дополнительными полями */,
message.getSecure() /* SessionContext */,
null /* вложения */);
/**
* выполняем
*/
}
message.create(true);
if (createMessage != null) createMessage.create(false);
return message;
}
}
Этот триггер нужно подключить в настройках тех операций, которые вы хотите отслеживать. Название операции можно, конечно, поменять. Не забудьте тогда сменить его и в обоих триггерах.
| Attachment | Size |
|---|---|
| log-scripts-classes.zip | 7.6 KB |
| log-scripts-src.zip | 4.4 KB |