Как сохранить историю изменения параметров задачи
В 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 |