Bug 377485: Implement editworkflow.cgi - Patch by Frédéric Buclin <LpSolit@gmail.com> r=gerv r=mkanat a=mkanat
git-svn-id: svn://10.0.0.236/trunk@226574 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
e7dcf6345f
commit
11e738fb2f
@ -583,6 +583,7 @@ use constant ABSTRACT_SCHEMA => {
|
||||
sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0},
|
||||
isactive => {TYPE => 'BOOLEAN', NOTNULL => 1,
|
||||
DEFAULT => 'TRUE'},
|
||||
is_open => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'},
|
||||
],
|
||||
INDEXES => [
|
||||
bug_status_value_idx => {FIELDS => ['value'],
|
||||
@ -671,6 +672,19 @@ use constant ABSTRACT_SCHEMA => {
|
||||
],
|
||||
},
|
||||
|
||||
status_workflow => {
|
||||
FIELDS => [
|
||||
# On bug creation, there is no old value.
|
||||
old_status => {TYPE => 'INT2'},
|
||||
new_status => {TYPE => 'INT2', NOTNULL => 1},
|
||||
require_comment => {TYPE => 'INT1', NOTNULL => 1, DEFAULT => 0},
|
||||
],
|
||||
INDEXES => [
|
||||
status_workflow_idx => {FIELDS => ['old_status', 'new_status'],
|
||||
TYPE => 'UNIQUE'},
|
||||
],
|
||||
},
|
||||
|
||||
# USER INFO
|
||||
# ---------
|
||||
|
||||
|
||||
@ -506,6 +506,9 @@ sub update_table_definitions {
|
||||
_fix_uppercase_custom_field_names();
|
||||
_fix_uppercase_index_names();
|
||||
|
||||
# 2007-05-17 LpSolit@gmail.com - Bug 344965
|
||||
_initialize_workflow();
|
||||
|
||||
################################################################
|
||||
# New --TABLE-- changes should go *** A B O V E *** this point #
|
||||
################################################################
|
||||
@ -2775,6 +2778,90 @@ sub _fix_uppercase_index_names {
|
||||
}
|
||||
}
|
||||
|
||||
sub _initialize_workflow {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
if (!$dbh->bz_column_info('bug_status', 'is_open')) {
|
||||
$dbh->bz_add_column('bug_status', 'is_open',
|
||||
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE'});
|
||||
|
||||
# Till now, bug statuses were not customizable. Nevertheless, local
|
||||
# changes are possible and so we will try to respect these changes.
|
||||
# This means: get the status of bugs having a resolution different from ''
|
||||
# and mark these statuses as 'closed', even if some of these statuses are
|
||||
# expected to be open statuses. Bug statuses we have no information about
|
||||
# are left as 'open'.
|
||||
my @statuses =
|
||||
@{$dbh->selectcol_arrayref('SELECT DISTINCT bug_status FROM bugs
|
||||
WHERE resolution != ?', undef, '')};
|
||||
|
||||
# Append the default list of closed statuses. Duplicated statuses don't hurt.
|
||||
@statuses = map {$dbh->quote($_)} (@statuses, qw(RESOLVED VERIFIED CLOSED));
|
||||
|
||||
print "Marking closed bug statuses as such...\n";
|
||||
$dbh->do('UPDATE bug_status SET is_open = 0 WHERE value IN (' .
|
||||
join(', ', @statuses) . ')');
|
||||
}
|
||||
|
||||
# Populate the status_workflow table. We do nothing if the table already
|
||||
# has entries. If all bug status transitions have been deleted, the
|
||||
# workflow will be restored to its default schema.
|
||||
my $count = $dbh->selectrow_array('SELECT COUNT(*) FROM status_workflow');
|
||||
return if $count;
|
||||
|
||||
my $create = Bugzilla->params->{'commentoncreate'};
|
||||
my $confirm = Bugzilla->params->{'commentonconfirm'};
|
||||
my $accept = Bugzilla->params->{'commentonaccept'};
|
||||
my $resolve = Bugzilla->params->{'commentonresolve'};
|
||||
my $verify = Bugzilla->params->{'commentonverify'};
|
||||
my $close = Bugzilla->params->{'commentonclose'};
|
||||
my $reopen = Bugzilla->params->{'commentonreopen'};
|
||||
# This was till recently the only way to get back to NEW for
|
||||
# confirmed bugs, so we use this parameter here.
|
||||
my $reassign = Bugzilla->params->{'commentonreassign'};
|
||||
|
||||
# This is the default workflow.
|
||||
my @workflow = ([undef, 'UNCONFIRMED', $create],
|
||||
[undef, 'NEW', $create],
|
||||
[undef, 'ASSIGNED', $create],
|
||||
['UNCONFIRMED', 'NEW', $confirm],
|
||||
['UNCONFIRMED', 'ASSIGNED', $accept],
|
||||
['UNCONFIRMED', 'RESOLVED', $resolve],
|
||||
['NEW', 'ASSIGNED', $accept],
|
||||
['NEW', 'RESOLVED', $resolve],
|
||||
['ASSIGNED', 'NEW', $reassign],
|
||||
['ASSIGNED', 'RESOLVED', $resolve],
|
||||
['REOPENED', 'NEW', $reassign],
|
||||
['REOPENED', 'ASSIGNED', $accept],
|
||||
['REOPENED', 'RESOLVED', $resolve],
|
||||
['RESOLVED', 'UNCONFIRMED', $reopen],
|
||||
['RESOLVED', 'REOPENED', $reopen],
|
||||
['RESOLVED', 'VERIFIED', $verify],
|
||||
['RESOLVED', 'CLOSED', $close],
|
||||
['VERIFIED', 'UNCONFIRMED', $reopen],
|
||||
['VERIFIED', 'REOPENED', $reopen],
|
||||
['VERIFIED', 'CLOSED', $close],
|
||||
['CLOSED', 'UNCONFIRMED', $reopen],
|
||||
['CLOSED', 'REOPENED', $reopen]);
|
||||
|
||||
print "Now filling the 'status_workflow' table with valid bug status transitions...\n";
|
||||
my $sth_select = $dbh->prepare('SELECT id FROM bug_status WHERE value = ?');
|
||||
my $sth = $dbh->prepare('INSERT INTO status_workflow (old_status, new_status,
|
||||
require_comment) VALUES (?, ?, ?)');
|
||||
|
||||
foreach my $transition (@workflow) {
|
||||
my ($from, $to);
|
||||
# If it's an initial state, there is no "old" value.
|
||||
$from = $dbh->selectrow_array($sth_select, undef, $transition->[0])
|
||||
if $transition->[0];
|
||||
$to = $dbh->selectrow_array($sth_select, undef, $transition->[1]);
|
||||
# If one of the bug statuses doesn't exist, the transition is invalid.
|
||||
next if (($transition->[0] && !$from) || !$to);
|
||||
|
||||
$sth->execute($from, $to, $transition->[2] ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
147
mozilla/webtools/bugzilla/editworkflow.cgi
Executable file
147
mozilla/webtools/bugzilla/editworkflow.cgi
Executable file
@ -0,0 +1,147 @@
|
||||
#!/usr/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Frédéric Buclin.
|
||||
# Portions created by Frédéric Buclin are Copyright (C) 2007
|
||||
# Frédéric Buclin. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
|
||||
|
||||
use strict;
|
||||
|
||||
use lib qw(.);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Token;
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $user = Bugzilla->login(LOGIN_REQUIRED);
|
||||
|
||||
print $cgi->header();
|
||||
|
||||
$user->in_group('admin')
|
||||
|| ThrowUserError('auth_failure', {group => 'admin',
|
||||
action => 'modify',
|
||||
object => 'workflow'});
|
||||
|
||||
my $action = $cgi->param('action') || 'edit';
|
||||
my $token = $cgi->param('token');
|
||||
|
||||
sub get_statuses {
|
||||
my $statuses = $dbh->selectall_arrayref('SELECT id, value, is_open FROM bug_status
|
||||
ORDER BY sortkey, value', { Slice => {} });
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
sub get_workflow {
|
||||
my $workflow = $dbh->selectall_arrayref('SELECT old_status, new_status, require_comment
|
||||
FROM status_workflow');
|
||||
my %workflow;
|
||||
foreach my $row (@$workflow) {
|
||||
my ($old, $new, $type) = @$row;
|
||||
$workflow{$old || 0}{$new} = $type;
|
||||
}
|
||||
return \%workflow;
|
||||
}
|
||||
|
||||
sub load_template {
|
||||
my ($filename, $message) = @_;
|
||||
my $template = Bugzilla->template;
|
||||
my $vars = {};
|
||||
|
||||
$vars->{'statuses'} = get_statuses();
|
||||
$vars->{'workflow'} = get_workflow();
|
||||
$vars->{'token'} = issue_session_token("workflow_$filename");
|
||||
$vars->{'message'} = $message;
|
||||
|
||||
$template->process("admin/workflow/$filename.html.tmpl", $vars)
|
||||
|| ThrowTemplateError($template->error());
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action eq 'edit') {
|
||||
load_template('edit');
|
||||
}
|
||||
elsif ($action eq 'update') {
|
||||
check_token_data($token, 'workflow_edit');
|
||||
my $statuses = get_statuses;
|
||||
my $workflow = get_workflow();
|
||||
my $initial_state = {id => 0};
|
||||
|
||||
my $sth_insert = $dbh->prepare('INSERT INTO status_workflow (old_status, new_status)
|
||||
VALUES (?, ?)');
|
||||
my $sth_delete = $dbh->prepare('DELETE FROM status_workflow
|
||||
WHERE old_status = ? AND new_status = ?');
|
||||
my $sth_delnul = $dbh->prepare('DELETE FROM status_workflow
|
||||
WHERE old_status IS NULL AND new_status = ?');
|
||||
|
||||
foreach my $old ($initial_state, @$statuses) {
|
||||
# Hashes cannot have undef as a key, so we use 0. But the DB
|
||||
# must store undef, for referential integrity.
|
||||
my $old_id_for_db = $old->{'id'} || undef;
|
||||
foreach my $new (@$statuses) {
|
||||
next if $old->{'id'} == $new->{'id'};
|
||||
|
||||
if ($cgi->param('w_' . $old->{'id'} . '_' . $new->{'id'})) {
|
||||
$sth_insert->execute($old_id_for_db, $new->{'id'})
|
||||
unless defined $workflow->{$old->{'id'}}->{$new->{'id'}};
|
||||
}
|
||||
elsif ($old_id_for_db) {
|
||||
$sth_delete->execute($old_id_for_db, $new->{'id'});
|
||||
}
|
||||
else {
|
||||
$sth_delnul->execute($new->{'id'});
|
||||
}
|
||||
}
|
||||
}
|
||||
delete_token($token);
|
||||
load_template('edit', 'workflow_updated');
|
||||
}
|
||||
elsif ($action eq 'edit_comment') {
|
||||
load_template('comment');
|
||||
}
|
||||
elsif ($action eq 'update_comment') {
|
||||
check_token_data($token, 'workflow_comment');
|
||||
my $workflow = get_workflow();
|
||||
|
||||
my $sth_update = $dbh->prepare('UPDATE status_workflow SET require_comment = ?
|
||||
WHERE old_status = ? AND new_status = ?');
|
||||
my $sth_updnul = $dbh->prepare('UPDATE status_workflow SET require_comment = ?
|
||||
WHERE old_status IS NULL AND new_status = ?');
|
||||
|
||||
foreach my $old (keys %$workflow) {
|
||||
# Hashes cannot have undef as a key, so we use 0. But the DB
|
||||
# must store undef, for referential integrity.
|
||||
my $old_id_for_db = $old || undef;
|
||||
foreach my $new (keys %{$workflow->{$old}}) {
|
||||
my $comment_required = $cgi->param("c_${old}_$new") ? 1 : 0;
|
||||
next if ($workflow->{$old}->{$new} == $comment_required);
|
||||
if ($old_id_for_db) {
|
||||
$sth_update->execute($comment_required, $old_id_for_db, $new);
|
||||
}
|
||||
else {
|
||||
$sth_updnul->execute($comment_required, $new);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete_token($token);
|
||||
load_template('comment', 'workflow_updated');
|
||||
}
|
||||
else {
|
||||
ThrowCodeError("action_unrecognized", {action => $action});
|
||||
}
|
||||
@ -65,3 +65,41 @@ td.admin_links dt.forbidden a, td.admin_links dd.forbidden a {
|
||||
color: inherit;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.col-header {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.checkbox-cell {
|
||||
border: 1px black solid;
|
||||
}
|
||||
|
||||
/* Grey-green color */
|
||||
.open-status {
|
||||
color: #286;
|
||||
}
|
||||
|
||||
/* Brown-red color */
|
||||
.closed-status {
|
||||
color: #a63;
|
||||
}
|
||||
|
||||
/* Dark green color */
|
||||
.checked {
|
||||
background-color: #5b4;
|
||||
}
|
||||
|
||||
/* Dark red color */
|
||||
td.forbidden {
|
||||
background-color: #811;
|
||||
}
|
||||
|
||||
tr.highlight:hover {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
th.title {
|
||||
font-size: larger;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
[%# 1.0@bugzilla.org %]
|
||||
[%# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
|
||||
# Gervase Markham <gerv@mozilla.org>
|
||||
#%]
|
||||
|
||||
[% PROCESS "global/field-descs.none.tmpl" %]
|
||||
|
||||
[% INCLUDE global/header.html.tmpl
|
||||
title = "Edit Actions Triggered by the Workflow"
|
||||
style_urls = ['skins/standard/admin.css']
|
||||
%]
|
||||
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
function toggle_cell(cell) {
|
||||
if (cell.checked)
|
||||
cell.parentNode.className = "checkbox-cell checked";
|
||||
else
|
||||
cell.parentNode.className = "checkbox-cell";
|
||||
}
|
||||
//-->
|
||||
</script>
|
||||
|
||||
<p>
|
||||
This page allows you to define which status transitions require a comment
|
||||
by the user doing the change.
|
||||
</p>
|
||||
|
||||
<form id="workflow_form" method="POST" action="editworkflow.cgi">
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="2"> </th>
|
||||
<th colspan="[% statuses.size FILTER html %]" class="title">To</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th rowspan="[% statuses.size + 2 FILTER html %]" class="title">From</th>
|
||||
<th> </th>
|
||||
[% FOREACH status = statuses %]
|
||||
<th class="col-header[% status.is_open ? " open-status" : " closed-status" %]">
|
||||
[% status.value FILTER html %]
|
||||
</th>
|
||||
[% END %]
|
||||
</tr>
|
||||
|
||||
[%# This defines the entry point in the workflow %]
|
||||
[% p = [{id => 0, value => "{Start}", is_open => 1}] %]
|
||||
[% FOREACH status = p.merge(statuses) %]
|
||||
<tr class="highlight">
|
||||
<th align="right" class="[% status.is_open ? "open-status" : "closed-status" %]">
|
||||
[% status.value FILTER html %]
|
||||
</th>
|
||||
|
||||
[% FOREACH new_status = statuses %]
|
||||
[% IF workflow.${status.id}.${new_status.id}.defined %]
|
||||
<td align="center" class="checkbox-cell
|
||||
[% " checked" IF workflow.${status.id}.${new_status.id} %]"
|
||||
title="From [% status.value FILTER html %] to [% new_status.value FILTER html %]">
|
||||
<input type="checkbox" name="c_[% status.id %]_[% new_status.id %]"
|
||||
id="c_[% status.id %]_[% new_status.id %]" onclick="toggle_cell(this)"
|
||||
[% " checked='checked'" IF workflow.${status.id}.${new_status.id} %]>
|
||||
</td>
|
||||
[% ELSE %]
|
||||
<td class="checkbox-cell forbidden"> </td>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
|
||||
<p align="center">
|
||||
<input type="hidden" name="action" value="update_comment">
|
||||
<input type="hidden" name="token" value="[% token FILTER html %]">
|
||||
<input type="submit" value="Commit Changes"> -
|
||||
<a href="editworkflow.cgi?action=edit_comment">Cancel Changes</a> -
|
||||
<a href="editworkflow.cgi">View Current Workflow</a>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
[% INCLUDE global/footer.html.tmpl %]
|
||||
@ -0,0 +1,93 @@
|
||||
[%# 1.0@bugzilla.org %]
|
||||
[%# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
|
||||
# Gervase Markham <gerv@mozilla.org>
|
||||
#%]
|
||||
|
||||
[% PROCESS "global/field-descs.none.tmpl" %]
|
||||
|
||||
[% INCLUDE global/header.html.tmpl
|
||||
title = "Edit Workflow"
|
||||
style_urls = ['skins/standard/admin.css']
|
||||
%]
|
||||
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
function toggle_cell(cell) {
|
||||
if (cell.checked)
|
||||
cell.parentNode.className = "checkbox-cell checked";
|
||||
else
|
||||
cell.parentNode.className = "checkbox-cell";
|
||||
}
|
||||
//-->
|
||||
</script>
|
||||
|
||||
<p>
|
||||
This page allows you to define which status transitions are valid
|
||||
in your workflow.
|
||||
</p>
|
||||
|
||||
<form id="workflow_form" method="POST" action="editworkflow.cgi">
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="2"> </th>
|
||||
<th colspan="[% statuses.size FILTER html %]" class="title">To</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th rowspan="[% statuses.size + 2 FILTER html %]" class="title">From</th>
|
||||
<th> </th>
|
||||
[% FOREACH status = statuses %]
|
||||
<th class="col-header[% status.is_open ? " open-status" : " closed-status" %]">
|
||||
[% status.value FILTER html %]
|
||||
</th>
|
||||
[% END %]
|
||||
</tr>
|
||||
|
||||
[%# This defines the entry point in the workflow %]
|
||||
[% p = [{id => 0, value => "{Start}", is_open => 1}] %]
|
||||
[% FOREACH status = p.merge(statuses) %]
|
||||
<tr class="highlight">
|
||||
<th align="right" class="[% status.is_open ? "open-status" : "closed-status" %]">
|
||||
[% status.value FILTER html %]
|
||||
</th>
|
||||
|
||||
[% FOREACH new_status = statuses %]
|
||||
[% IF status.id != new_status.id %]
|
||||
<td align="center" class="checkbox-cell
|
||||
[% " checked" IF workflow.${status.id}.${new_status.id}.defined %]"
|
||||
title="From [% status.value FILTER html %] to [% new_status.value FILTER html %]">
|
||||
<input type="checkbox" name="w_[% status.id %]_[% new_status.id %]"
|
||||
id="w_[% status.id %]_[% new_status.id %]" onclick="toggle_cell(this)"
|
||||
[% " checked='checked'" IF workflow.${status.id}.${new_status.id}.defined %]>
|
||||
</td>
|
||||
[% ELSE %]
|
||||
<td class="checkbox-cell forbidden"> </td>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
|
||||
<p align="center">
|
||||
<input type="hidden" name="action" value="update">
|
||||
<input type="hidden" name="token" value="[% token FILTER html %]">
|
||||
<input type="submit" value="Commit Changes"> -
|
||||
<a href="editworkflow.cgi">Cancel Changes</a> -
|
||||
<a href="editworkflow.cgi?action=edit_comment">View Current Triggers</a>
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
[% INCLUDE global/footer.html.tmpl %]
|
||||
@ -543,6 +543,16 @@
|
||||
'comp.bug_count'
|
||||
],
|
||||
|
||||
'admin/workflow/edit.html.tmpl' => [
|
||||
'status.id',
|
||||
'new_status.id',
|
||||
],
|
||||
|
||||
'admin/workflow/comment.html.tmpl' => [
|
||||
'status.id',
|
||||
'new_status.id',
|
||||
],
|
||||
|
||||
'account/login.html.tmpl' => [
|
||||
'target',
|
||||
],
|
||||
|
||||
@ -491,7 +491,10 @@
|
||||
You entered a username that matched more than one
|
||||
user, so we have instead left the [% match_field FILTER html %]
|
||||
field blank.
|
||||
|
||||
|
||||
[% ELSIF message_tag == "workflow_updated" %]
|
||||
The workflow has been updated.
|
||||
|
||||
[% ELSE %]
|
||||
[%# Give sensible error if error functions are used incorrectly.
|
||||
#%]
|
||||
|
||||
@ -189,7 +189,7 @@
|
||||
[% ELSIF object == "settings" %]
|
||||
settings
|
||||
[% ELSIF object == "sudo_session" %]
|
||||
an sudo session
|
||||
a sudo session
|
||||
[% ELSIF object == "timetracking_summaries" %]
|
||||
time-tracking summary reports
|
||||
[% ELSIF object == "user" %]
|
||||
@ -198,6 +198,8 @@
|
||||
users
|
||||
[% ELSIF object == "versions" %]
|
||||
versions
|
||||
[% ELSIF object == "workflow" %]
|
||||
the workflow
|
||||
[% END %].
|
||||
|
||||
[% Hook.process("auth_failure") %]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user