summaryrefslogtreecommitdiff
path: root/rt/share/html/Elements
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2015-07-09 22:32:26 -0700
committerIvan Kohler <ivan@freeside.biz>2015-07-09 22:32:26 -0700
commit026dc7ad72ba972f230b6709e31fa64397d75ad4 (patch)
treec5af1a7ac9154744afc3660e9a9405892f2bb50b /rt/share/html/Elements
parent07b4bc84d1078f7390221d766cdb3142513db4b0 (diff)
parent1c538bfabc2cd31f27067505f0c3d1a46cba6ef0 (diff)
merge RT 4.2.11 and Header changes to disable RT javascript, RT#34237
Diffstat (limited to 'rt/share/html/Elements')
-rw-r--r--rt/share/html/Elements/AddLinks106
-rw-r--r--rt/share/html/Elements/BulkCustomFields105
-rw-r--r--rt/share/html/Elements/BulkLinks197
-rwxr-xr-xrt/share/html/Elements/Callback5
-rw-r--r--rt/share/html/Elements/CollectionAsTable/Header21
-rw-r--r--rt/share/html/Elements/CollectionAsTable/Row1
-rw-r--r--rt/share/html/Elements/CollectionList31
-rw-r--r--rt/share/html/Elements/CollectionListPaging20
-rw-r--r--rt/share/html/Elements/ColumnMap110
-rw-r--r--rt/share/html/Elements/Crypt/KeyIssues (renamed from rt/share/html/Elements/GnuPG/KeyIssues)14
-rw-r--r--rt/share/html/Elements/Crypt/SelectKeyForEncryption (renamed from rt/share/html/Elements/GnuPG/SelectKeyForEncryption)5
-rw-r--r--rt/share/html/Elements/Crypt/SelectKeyForSigning (renamed from rt/share/html/Elements/GnuPG/SelectKeyForSigning)7
-rw-r--r--rt/share/html/Elements/Crypt/SignEncryptWidget (renamed from rt/share/html/Elements/GnuPG/SignEncryptWidget)23
-rw-r--r--rt/share/html/Elements/CryptStatus195
-rw-r--r--rt/share/html/Elements/EditCustomField50
-rw-r--r--rt/share/html/Elements/EditCustomFieldAutocomplete15
-rw-r--r--rt/share/html/Elements/EditCustomFieldBinary17
-rw-r--r--rt/share/html/Elements/EditCustomFieldCombobox13
-rw-r--r--[-rwxr-xr-x]rt/share/html/Elements/EditCustomFieldCustomGroupings (renamed from rt/share/html/Elements/SelectTicketSortBy)33
-rw-r--r--rt/share/html/Elements/EditCustomFieldDate3
-rw-r--r--rt/share/html/Elements/EditCustomFieldDateTime3
-rw-r--r--rt/share/html/Elements/EditCustomFieldFreeform5
-rw-r--r--rt/share/html/Elements/EditCustomFieldImage17
-rw-r--r--rt/share/html/Elements/EditCustomFieldSelect31
-rw-r--r--rt/share/html/Elements/EditCustomFieldText6
-rw-r--r--rt/share/html/Elements/EditCustomFieldWikitext6
-rw-r--r--rt/share/html/Elements/EditCustomFields119
-rwxr-xr-xrt/share/html/Elements/EditLinks94
-rw-r--r--rt/share/html/Elements/EditTimeValue12
-rw-r--r--rt/share/html/Elements/EmailInput4
-rwxr-xr-xrt/share/html/Elements/Error13
-rw-r--r--[-rwxr-xr-x]rt/share/html/Elements/FindUser (renamed from rt/share/html/Elements/BevelBoxRaisedEnd)6
-rw-r--r--[-rwxr-xr-x]rt/share/html/Elements/FoldStanzaJS (renamed from rt/share/html/Elements/BevelBoxRaisedStart)6
-rw-r--r--[-rwxr-xr-x]rt/share/html/Elements/GotoUser (renamed from rt/share/html/Elements/SelectLinkType)19
-rwxr-xr-xrt/share/html/Elements/Header50
-rw-r--r--rt/share/html/Elements/HeaderJavascript20
-rw-r--r--rt/share/html/Elements/JavascriptConfig (renamed from rt/share/html/Elements/ShowUserConcise)55
-rwxr-xr-xrt/share/html/Elements/ListActions5
-rw-r--r--rt/share/html/Elements/ListMenu4
-rwxr-xr-xrt/share/html/Elements/Login7
-rw-r--r--[-rwxr-xr-x]rt/share/html/Elements/LoginHelp (renamed from rt/share/html/Elements/MyTickets)9
-rw-r--r--rt/share/html/Elements/Logo5
-rw-r--r--rt/share/html/Elements/MakeClicky18
-rwxr-xr-xrt/share/html/Elements/Menu36
-rwxr-xr-xrt/share/html/Elements/MessageBox26
-rw-r--r--rt/share/html/Elements/MyRT28
-rwxr-xr-xrt/share/html/Elements/MyReminders1
-rwxr-xr-xrt/share/html/Elements/MyRequests49
-rwxr-xr-xrt/share/html/Elements/PageLayout11
-rw-r--r--rt/share/html/Elements/QueryString3
-rw-r--r--rt/share/html/Elements/QueueSummaryByLifecycle13
-rw-r--r--rt/share/html/Elements/QueueSummaryByStatus4
-rw-r--r--rt/share/html/Elements/QuickCreate2
-rw-r--r--rt/share/html/Elements/RT__Article/ColumnMap13
-rw-r--r--rt/share/html/Elements/RT__Class/ColumnMap13
-rw-r--r--rt/share/html/Elements/RT__CustomField/ColumnMap39
-rw-r--r--rt/share/html/Elements/RT__Dashboard/ColumnMap9
-rw-r--r--rt/share/html/Elements/RT__Group/ColumnMap21
-rw-r--r--rt/share/html/Elements/RT__Queue/ColumnMap21
-rw-r--r--rt/share/html/Elements/RT__SavedSearch/ColumnMap9
-rw-r--r--rt/share/html/Elements/RT__Scrip/ColumnMap110
-rw-r--r--rt/share/html/Elements/RT__Template/ColumnMap35
-rw-r--r--rt/share/html/Elements/RT__Ticket/ColumnMap94
-rw-r--r--rt/share/html/Elements/RT__User/ColumnMap13
-rwxr-xr-xrt/share/html/Elements/Refresh2
-rwxr-xr-xrt/share/html/Elements/SelectBoolean4
-rwxr-xr-xrt/share/html/Elements/SelectCustomFieldValue17
-rwxr-xr-xrt/share/html/Elements/SelectDate18
-rwxr-xr-xrt/share/html/Elements/SelectDateRelation2
-rwxr-xr-xrt/share/html/Elements/SelectLang2
-rwxr-xr-xrt/share/html/Elements/SelectMatch10
-rw-r--r--rt/share/html/Elements/SelectObject141
-rw-r--r--rt/share/html/Elements/SelectOwnerAutocomplete2
-rw-r--r--rt/share/html/Elements/SelectOwnerDropdown32
-rwxr-xr-xrt/share/html/Elements/SelectQueue92
-rwxr-xr-xrt/share/html/Elements/SelectStatus58
-rwxr-xr-xrt/share/html/Elements/SelectTicketTypes58
-rwxr-xr-xrt/share/html/Elements/SelectTimeUnits6
-rw-r--r--[-rwxr-xr-x]rt/share/html/Elements/ShowCustomFieldCustomGroupings (renamed from rt/share/html/Elements/SelectSortOrder)41
-rw-r--r--rt/share/html/Elements/ShowCustomFieldImage2
-rw-r--r--rt/share/html/Elements/ShowCustomFields20
-rw-r--r--rt/share/html/Elements/ShowHistory191
-rw-r--r--rt/share/html/Elements/ShowLink25
-rwxr-xr-xrt/share/html/Elements/ShowLinks155
-rw-r--r--rt/share/html/Elements/ShowLinksOfType127
-rw-r--r--rt/share/html/Elements/ShowMemberships4
-rw-r--r--rt/share/html/Elements/ShowMessageHeaders101
-rw-r--r--rt/share/html/Elements/ShowMessageStanza176
-rw-r--r--rt/share/html/Elements/ShowPrincipal (renamed from rt/share/html/Elements/ShowUserVerbose)42
-rw-r--r--rt/share/html/Elements/ShowRecord100
-rw-r--r--rt/share/html/Elements/ShowRelationLabel30
-rw-r--r--rt/share/html/Elements/ShowReminders8
-rw-r--r--rt/share/html/Elements/ShowSearch21
-rw-r--r--rt/share/html/Elements/ShowTransaction265
-rw-r--r--rt/share/html/Elements/ShowTransactionAttachments293
-rw-r--r--rt/share/html/Elements/ShowUser53
-rwxr-xr-xrt/share/html/Elements/SimpleSearch5
-rwxr-xr-xrt/share/html/Elements/Submit4
-rw-r--r--rt/share/html/Elements/TSVExport131
-rwxr-xr-xrt/share/html/Elements/Tabs270
-rw-r--r--rt/share/html/Elements/TicketList12
-rw-r--r--rt/share/html/Elements/TitleBox1
-rw-r--r--rt/share/html/Elements/TitleBoxEnd1
-rw-r--r--rt/share/html/Elements/TitleBoxStart1
-rw-r--r--rt/share/html/Elements/ValidateCustomFields73
105 files changed, 3495 insertions, 1136 deletions
diff --git a/rt/share/html/Elements/AddLinks b/rt/share/html/Elements/AddLinks
new file mode 100644
index 0000000..3e34237
--- /dev/null
+++ b/rt/share/html/Elements/AddLinks
@@ -0,0 +1,106 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%args>
+$Object => undef
+$CustomFields => undef
+$ARGSRef => $DECODED_ARGS
+</%args>
+<%init>
+my $id = ($Object and $Object->id)
+ ? $Object->id
+ : "new";
+
+my $exclude = qq| data-autocomplete="Tickets" data-autocomplete-multiple="1"|;
+$exclude .= qq| data-autocomplete-exclude="$id"| if $Object->id;
+</%init>
+% if (ref($Object) eq 'RT::Ticket') {
+<i><&|/l&>Enter tickets or URIs to link tickets to. Separate multiple entries with spaces.</&>
+<br /><&|/l&>You may enter links to Articles as "a:###", where ### represents the number of the Article.</&>
+% $m->callback( CallbackName => 'ExtraLinkInstructions' );
+</i><br />
+% } elsif (ref($Object) eq 'RT::Queue') {
+<i><&|/l&>Enter queues or URIs to link queues to. Separate multiple entries with spaces.</&>
+</i><br />
+% } else {
+<i><&|/l&>Enter objects or URIs to link objects to. Separate multiple entries with spaces.</&></i><br />
+% }
+<table>
+ <tr>
+ <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Depends on').':', Relation => 'DependsOn' &></td>
+ <td class="entry"><input name="<%$id%>-DependsOn" value="<% $ARGSRef->{"$id-DependsOn"} || '' %>" <% $exclude |n%>/></td>
+ </tr>
+ <tr>
+ <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Depended on by').':', Relation => 'DependedOnBy' &></td>
+ <td class="entry"><input name="DependsOn-<%$id%>" value="<% $ARGSRef->{"DependsOn-$id"} || '' %>" <% $exclude |n%>/></td>
+ </tr>
+ <tr>
+ <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Parents').':', Relation => 'Parents' &></td>
+ <td class="entry"><input name="<%$id%>-MemberOf" value="<% $ARGSRef->{"$id-MemberOf"} || '' %>" <% $exclude |n%>/></td>
+ </tr>
+ <tr>
+ <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Children').':', Relation => 'Children' &></td>
+ <td class="entry"> <input name="MemberOf-<%$id%>" value="<% $ARGSRef->{"MemberOf-$id"} || '' %>" <% $exclude |n%>/></td>
+ </tr>
+ <tr>
+ <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Refers to').':', Relation => 'RefersTo' &></td>
+ <td class="entry"><input name="<%$id%>-RefersTo" value="<% $ARGSRef->{"$id-RefersTo"} || '' %>" <% $exclude |n%>/></td>
+ </tr>
+ <tr>
+ <td class="label"><& ShowRelationLabel, Object => $Object, Label => loc('Referred to by').':', Relation => 'ReferredToBy' &></td>
+ <td class="entry"> <input name="RefersTo-<%$id%>" value="<% $ARGSRef->{"RefersTo-$id"} || '' %>" <% $exclude |n%>/></td>
+ </tr>
+ <& /Elements/EditCustomFields,
+ Object => $Object,
+ Grouping => 'Links',
+ InTable => 1,
+ ($CustomFields
+ ? (CustomFields => $CustomFields)
+ : ()),
+ &>
+% $m->callback( CallbackName => 'NewLink' );
+</table>
diff --git a/rt/share/html/Elements/BulkCustomFields b/rt/share/html/Elements/BulkCustomFields
new file mode 100644
index 0000000..871e306
--- /dev/null
+++ b/rt/share/html/Elements/BulkCustomFields
@@ -0,0 +1,105 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<table class="bulk-edit-custom-fields">
+
+<tr>
+<th><&|/l&>Name</&></th>
+<th><&|/l&>Add values</&></th>
+<th><&|/l&>Delete values</&></th>
+</tr>
+% my $i = 0;
+% while (my $cf = $CustomFields->Next) {
+<tr class="<% ++$i%2 ? 'oddline': 'evenline' %>">
+<td class="label"><% $cf->Name %><br />
+<em>(<% $cf->FriendlyType %>)</em></td>
+% my $rows = 5;
+% my $cf_id = $cf->id;
+% my @add = (NamePrefix => 'Bulk-Add-CustomField-', CustomField => $cf, Rows => $rows,
+% Multiple => ($cf->MaxValues ==1 ? 0 : 1) , Cols => 25,
+% Default => $ARGS{"Bulk-Add-CustomField-$cf_id-Values"} || $ARGS{"Bulk-Add-CustomField-$cf_id-Value"}, );
+% my @del = (NamePrefix => 'Bulk-Delete-CustomField-', CustomField => $cf,
+% Rows => $rows, Multiple => 1, Cols => 25,
+% Default => $ARGS{"Bulk-Delete-CustomField-$cf_id-Values"} || $ARGS{"Bulk-Delete-CustomField-$cf_id-Value"}, );
+% if ($cf->Type eq 'Select') {
+<td><& /Elements/EditCustomFieldSelect, @add &></td>
+<td><& /Elements/EditCustomFieldSelect, @del &><br />
+% } elsif ($cf->Type eq 'Combobox') {
+<td><& /Elements/EditCustomFieldCombobox, @add &></td>
+<td><& /Elements/EditCustomFieldCombobox, @del &><br />
+% } elsif ($cf->Type eq 'Freeform') {
+<td><& /Elements/EditCustomFieldFreeform, @add &></td>
+<td><& /Elements/EditCustomFieldFreeform, @del &><br />
+% } elsif ($cf->Type eq 'Text') {
+<td><& /Elements/EditCustomFieldText, @add &></td>
+<td>
+% } elsif ($cf->Type eq 'Wikitext') {
+<td><& /Elements/EditCustomFieldWikitext, @add &></td>
+<td>
+% } elsif ($cf->Type eq 'Date') {
+<td><& /Elements/EditCustomFieldDate, @add, Default => undef &></td>
+<td><& /Elements/EditCustomFieldDate, @del, Default => undef &><br />
+% } elsif ($cf->Type eq 'DateTime') {
+% # Pass datemanip format to prevent another tz date conversion
+<td><& /Elements/EditCustomFieldDateTime, @add, Default => undef, Format => 'datemanip' &></td>
+<td><& /Elements/EditCustomFieldDateTime, @del, Default => undef, Format => 'datemanip' &><br />
+% } else {
+% $RT::Logger->crit("Unknown CustomField type: " . $cf->Type);
+% next
+% }
+ <label><input type="checkbox" name="Bulk-Delete-CustomField-<% $cf_id %>-AllValues" value="1">
+ <em><&|/l&>(Check to delete all values)</&></em></label>
+</td>
+</tr>
+% }
+</table>
+<%ARGS>
+$CustomFields
+</%ARGS>
+<%INIT>
+return unless $CustomFields->Count;
+</%INIT>
diff --git a/rt/share/html/Elements/BulkLinks b/rt/share/html/Elements/BulkLinks
new file mode 100644
index 0000000..473e843
--- /dev/null
+++ b/rt/share/html/Elements/BulkLinks
@@ -0,0 +1,197 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<table width="100%">
+ <tr>
+ <td valign="top" width="50%">
+ <h3><&|/l&>Current Links</&></h3>
+<table>
+ <tr>
+ <td class="labeltop"><&|/l&>Depends on</&>:</td>
+ <td class="value">
+% if ( $hash{DependsOn} ) {
+% for my $link ( values %{$hash{DependsOn}} ) {
+ <input type="checkbox" class="checkbox" id="DeleteLink--<%$link->Type%>-<%$link->Target%>" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <label for="DeleteLink--<%$link->Type%>-<%$link->Target%>"><& /Elements/ShowLink, URI => $link->TargetURI &></label><br />
+% } }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Depended on by</&>:</td>
+ <td class="value">
+% if ( $hash{DependedOnBy} ) {
+% for my $link ( values %{$hash{DependedOnBy}} ) {
+ <input type="checkbox" class="checkbox" id="DeleteLink-<%$link->Base%>-<%$link->Type%>-" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <label for="DeleteLink-<%$link->Base%>-<%$link->Type%>-"><& /Elements/ShowLink, URI => $link->BaseURI &></label><br />
+% } }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Parents</&>:</td>
+ <td class="value">
+% if ( $hash{MemberOf} ) {
+% for my $link ( values %{$hash{MemberOf}} ) {
+ <input type="checkbox" class="checkbox" id="DeleteLink--<%$link->Type%>-<%$link->Target%>" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <label for="DeleteLink--<%$link->Type%>-<%$link->Target%>"><& /Elements/ShowLink, URI => $link->TargetURI &></label><br />
+% } }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Children</&>:</td>
+ <td class="value">
+% if ( $hash{Members} ) {
+% for my $link ( values %{$hash{Members}} ) {
+ <input type="checkbox" class="checkbox" id="DeleteLink-<%$link->Base%>-<%$link->Type%>-" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <label for="DeleteLink-<%$link->Base%>-<%$link->Type%>-"><& /Elements/ShowLink, URI => $link->BaseURI &></label><br />
+% } }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Refers to</&>:</td>
+ <td class="value">
+% if ( $hash{RefersTo} ) {
+% for my $link ( values %{$hash{RefersTo}} ) {
+ <input type="checkbox" class="checkbox" id="DeleteLink--<%$link->Type%>-<%$link->Target%>" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <label for="DeleteLink--<%$link->Type%>-<%$link->Target%>"><& /Elements/ShowLink, URI => $link->TargetURI &></label><br />
+% } }
+ </td>
+ </tr>
+ <tr>
+ <td class="labeltop"><&|/l&>Referred to by</&>:</td>
+ <td class="value">
+% if ( $hash{ReferredToBy} ) {
+% for my $link ( values %{$hash{ReferredToBy}} ) {
+% # Skip reminders
+% next if (UNIVERSAL::isa($link->BaseObj, 'RT::Ticket') && $link->BaseObj->Type eq 'reminder');
+ <input type="checkbox" class="checkbox" id="DeleteLink-<%$link->Base%>-<%$link->Type%>-" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <label for="DeleteLink-<%$link->Base%>-<%$link->Type%>-"><& /Elements/ShowLink, URI => $link->BaseURI &></label><br />
+% } }
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><i><&|/l&>(Check box to delete)</&></i></td>
+ </tr>
+</table>
+</td>
+<td valign="top">
+<h3><&|/l&>New Links</&></h3>
+<em><&|/l&>Enter tickets or URIs to link to. Separate multiple entries with spaces.</&>
+</em><br />
+<table>
+ <tr>
+ <td class="label"><&|/l&>Depends on</&>:</td>
+ <td class="entry"><input name="<% $record_type %>-DependsOn" data-autocomplete="Tickets" data-autocomplete-multiple="1" value="<% $ARGS{ $record_type .'-DependsOn' } || '' %>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Depended on by</&>:</td>
+ <td class="entry"><input name="DependsOn-<% $record_type %>" data-autocomplete="Tickets" data-autocomplete-multiple="1" value="<% $ARGS{ 'DependsOn-'. $record_type } || '' %>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Parents</&>:</td>
+ <td class="entry"><input name="<% $record_type %>-MemberOf" data-autocomplete="Tickets" data-autocomplete-multiple="1" value="<% $ARGS{ $record_type .'-MemberOf'} || '' %>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Children</&>:</td>
+ <td class="entry"> <input name="MemberOf-<% $record_type %>" data-autocomplete="Tickets" data-autocomplete-multiple="1" value="<% $ARGS{ 'MemberOf-'. $record_type } || '' %>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Refers to</&>:</td>
+ <td class="entry"><input name="<% $record_type %>-RefersTo" data-autocomplete="Tickets" data-autocomplete-multiple="1" value="<% $ARGS{ $record_type .'-RefersTo'} || '' %>" /></td>
+ </tr>
+ <tr>
+ <td class="label"><&|/l&>Referred to by</&>:</td>
+ <td class="entry"> <input name="RefersTo-<% $record_type %>" data-autocomplete="Tickets" data-autocomplete-multiple="1" value="<% $ARGS{ 'RefersTo-'. $record_type } || '' %>" /></td>
+ </tr>
+</table>
+</td>
+</tr>
+</table>
+
+<%ARGS>
+$Collection
+</%ARGS>
+
+<%INIT>
+
+my @types = qw/DependsOn DependedOnBy Members MemberOf RefersTo ReferredToBy/;
+
+my $record_type = $Collection->RecordClass;
+$record_type =~ s/^RT:://; $record_type =~ s/::/-/g;
+
+my %hash;
+if ( $Collection->Count ) {
+ my $first_record = $Collection->Next;
+ # we only show current links that exist on all the records
+ for my $type ( @types ) {
+ my $target_or_base =
+ $type =~ /DependsOn|MemberOf|RefersTo/ ? 'Target' : 'Base';
+ my $links = $first_record->$type;
+ while ( my $link = $links->Next ) {
+ $hash{$type}{$link->$target_or_base} = $link;
+ }
+ }
+
+ while ( my $record = $Collection->Next ) {
+ for my $type ( @types ) {
+ my $target_or_base =
+ $type =~ /DependsOn|MemberOf|RefersTo/ ? 'Target' : 'Base';
+ # if $hash{$type} is empty, no need to check any more
+ next unless $hash{$type} && keys %{$hash{$type}};
+
+ my %exists;
+ while ( my $link = $record->$type->Next ) {
+ $exists{$link->$target_or_base}++;
+ }
+
+ for ( keys %{$hash{$type}} ) {
+ delete $hash{$type}{$_} unless $exists{$_};
+ }
+ }
+ }
+}
+</%INIT>
diff --git a/rt/share/html/Elements/Callback b/rt/share/html/Elements/Callback
index 119853a..d472e14 100755
--- a/rt/share/html/Elements/Callback
+++ b/rt/share/html/Elements/Callback
@@ -48,6 +48,9 @@
<%INIT>
$ARGS{'CallbackPage'} = delete $ARGS{'Page'} || $m->callers(1)->path;
$ARGS{'CallbackName'} = delete $ARGS{'_CallbackName'};
-$RT::Logger->debug("$ARGS{'CallbackPage'} calls old style callback, use \$m->callback");
+RT->Deprecated(
+ Instead => '$m->callback',
+ Remove => "4.4",
+);
return $m->callback( %ARGS );
</%INIT>
diff --git a/rt/share/html/Elements/CollectionAsTable/Header b/rt/share/html/Elements/CollectionAsTable/Header
index ea3fafd..a1c5c48 100644
--- a/rt/share/html/Elements/CollectionAsTable/Header
+++ b/rt/share/html/Elements/CollectionAsTable/Header
@@ -71,6 +71,8 @@ $generic_query_args->{'Format'} = $FormatString if grep $_ eq 'Format', @PassArg
my $item = 0;
foreach my $col ( @Format ) {
+ my $attr = $col->{'attribute'} || $col->{'last_attribute'};
+
my $title = $col->{'title'} || '';
if ( $title eq 'NEWLINE' ) {
while ( $item < $maxitems ) {
@@ -93,12 +95,22 @@ foreach my $col ( @Format ) {
$m->out('<th class="collection-as-table"');
$m->out(' colspan="' . $m->interp->apply_escapes($span => 'h') . '"')
if $span;
+
+ my $align = $col->{'align'} || do {
+ my $tmp_columnmap = $m->comp( '/Elements/ColumnMap',
+ Class => $Class,
+ Name => $attr,
+ Attr => 'align',
+ );
+ ProcessColumnMapValue( $tmp_columnmap, Arguments => [ $attr ] );
+ };
+ $m->out(qq{ style="text-align: $align"}) if $align;
$m->out('>');
+ my $loc_title;
# if title is not defined then use defined attribute or last
# one we saw in the format
unless ( defined $col->{'title'} ) {
- my $attr = $col->{'attribute'} || $col->{'last_attribute'};
my $tmp = $m->comp( '/Elements/ColumnMap',
Class => $Class,
Name => $attr,
@@ -109,8 +121,9 @@ foreach my $col ( @Format ) {
# in case title is not defined in ColumnMap
# the following regex changes $attr like from "ReferredToBy" to "Referred To By"
$title = join ' ', split /(?<=[a-z])(?=[A-Z])/, $attr unless defined $title;
+ $loc_title = $attr =~ /^(?:CustomField|CF)\./ ? $title : loc($title);
} else {
- $title = $m->comp('/Elements/ScrubHTML', Content => $title);
+ $loc_title = loc($m->comp('/Elements/ScrubHTML', Content => $title));
}
if ( $AllowSorting and $col->{'attribute'}
@@ -134,11 +147,11 @@ foreach my $col ( @Format ) {
%$generic_query_args,
OrderBy => $attr, Order => $new_order
), 'h')
- . '">'. loc($title) .'</a>'
+ . '">'. $loc_title .'</a>'
);
}
else {
- $m->out( loc($title) );
+ $m->out( $loc_title );
}
$m->out('</th>');
}
diff --git a/rt/share/html/Elements/CollectionAsTable/Row b/rt/share/html/Elements/CollectionAsTable/Row
index b83943a..deaa312 100644
--- a/rt/share/html/Elements/CollectionAsTable/Row
+++ b/rt/share/html/Elements/CollectionAsTable/Row
@@ -130,7 +130,6 @@ foreach my $column (@Format) {
Arguments => [$record, $i],
);
}
- s/\n/<br \/>/gs for grep defined $_, @out;
$m->out( $_ .'="'. $m->interp->apply_escapes( $attrs{$_} => 'h' ) .'"' )
foreach grep $attrs{$_}, qw(align style colspan);
diff --git a/rt/share/html/Elements/CollectionList b/rt/share/html/Elements/CollectionList
index d76cf01..fc678f4 100644
--- a/rt/share/html/Elements/CollectionList
+++ b/rt/share/html/Elements/CollectionList
@@ -113,23 +113,24 @@ if ($Class =~ /::/) { # older passed in value
}
$m->out('<table cellspacing="0" class="' .
- ($Collection->isa('RT::Tickets') ? 'ticket-list' : 'collection') . ' collection-as-table">');
+ ($Collection->isa('RT::Tickets') ? 'ticket-list' : 'collection') . ' collection-as-table">');
if ( $ShowHeader ) {
- $m->comp('/Elements/CollectionAsTable/Header',
- %ARGS,
- Class => $Class,
- Format => \@Format,
- FormatString => $Format,
- Order => \@Order,
- OrderBy => \@OrderBy,
- Rows => $Rows,
- Page => $Page,
- AllowSorting => $AllowSorting,
- BaseURL => $BaseURL,
- GenericQueryArgs => $GenericQueryArgs,
- maxitems => $maxitems,
- );
+ $m->comp('/Elements/CollectionAsTable/Header',
+ %ARGS,
+ Class => $Class,
+ Format => \@Format,
+ FormatString => $Format,
+ Order => \@Order,
+ OrderBy => \@OrderBy,
+ Rows => $Rows,
+ Page => $Page,
+ AllowSorting => $AllowSorting,
+ BaseURL => $BaseURL,
+ GenericQueryArgs => $GenericQueryArgs,
+ maxitems => $maxitems,
+ PassArguments => \@PassArguments,
+ );
}
my ($i, $column_map) = (0, {});
diff --git a/rt/share/html/Elements/CollectionListPaging b/rt/share/html/Elements/CollectionListPaging
index 8bfa4f6..a7f2aee 100644
--- a/rt/share/html/Elements/CollectionListPaging
+++ b/rt/share/html/Elements/CollectionListPaging
@@ -64,15 +64,15 @@ if ($Pages == 1) {
else{
$m->out(loc('Page') . ' ');
my $prev = $m->interp->apply_escapes($m->comp(
- '/Elements/QueryString',
- %$URLParams,
- Page => ( $CurrentPage - 1 )
- ), 'h');
+ '/Elements/QueryString',
+ %$URLParams,
+ Page => ( $CurrentPage - 1 )
+ ), 'h');
my $next = $m->interp->apply_escapes($m->comp(
- '/Elements/QueryString',
- %$URLParams,
- Page => ( $CurrentPage + 1 )
- ), 'h');
+ '/Elements/QueryString',
+ %$URLParams,
+ Page => ( $CurrentPage + 1 )
+ ), 'h');
my %show;
$show{1} = 1;
$show{$_} = 1 for (($CurrentPage - 2)..($CurrentPage + 2));
@@ -84,7 +84,7 @@ for my $number ( 1 .. $Pages ) {
$dots = undef;
my $qs =
$m->interp->apply_escapes($m->comp( '/Elements/QueryString', %$URLParams, Page => $number ), 'h');
- $m->out(qq{<span class="pagenum">});
+ $m->out(qq{<span class="pagenum">});
if ( $number == $CurrentPage ) {
$m->out(qq{<span class="currentpage">$number</span> });
}
@@ -96,7 +96,7 @@ for my $number ( 1 .. $Pages ) {
$dots = 1;
$m->out(qq{<span class="dots">...</span>});
}
- $m->out(qq{</span>});
+ $m->out(qq{</span>});
}
if ($CurrentPage > 1) {
diff --git a/rt/share/html/Elements/ColumnMap b/rt/share/html/Elements/ColumnMap
index 3ae10ea..9c65825 100644
--- a/rt/share/html/Elements/ColumnMap
+++ b/rt/share/html/Elements/ColumnMap
@@ -52,11 +52,14 @@ $Attr => undef
</%ARGS>
<%ONCE>
+use Scalar::Util;
+
# This is scary and should totally be refactored -- jesse
-my $COLUMN_MAP = {
+my ($COLUMN_MAP, $WCOLUMN_MAP);
+$WCOLUMN_MAP = $COLUMN_MAP = {
id => {
attribute => 'id',
- title => 'id', # loc
+ title => '#', # loc
align => 'right',
value => sub { return $_[0]->id }
},
@@ -96,6 +99,12 @@ my $COLUMN_MAP = {
attribute => sub { return shift @_ },
title => sub { return pop @_ },
value => sub {
+ my $self = $WCOLUMN_MAP->{CustomField};
+ my $cf = $self->{load}->(@_);
+ return unless $cf->Id;
+ return $self->{render}->( $cf, $cf->ValuesForObject($_[0])->ItemsArrayRef );
+ },
+ load => sub {
# Cache the CF object on a per-request basis, to avoid
# having to load it for every row
my $key = join("-","CF",
@@ -106,36 +115,31 @@ my $COLUMN_MAP = {
my $cf = $m->notes($key);
unless ($cf) {
$cf = $_[0]->LoadCustomFieldByIdentifier($_[-1]);
+ RT->Logger->notice("Unable to load $_[-1] for ".$_[0]->CustomFieldLookupType." ".$_[0]->CustomFieldLookupId)
+ unless $cf->Id;
$m->notes($key, $cf);
}
- # Display custom field contents, separated by newlines.
- # For Image custom fields we also show a thumbnail here.
- my $values = $cf->ValuesForObject( $_[0] );
- return if $values->Count == 0;
-
- my @values;
- # it is guaranteed to be the same type for all fields, right?
- my $v = $values->First;
- my $cftype = $v->CustomFieldObj->Type;
-
- do {
- if ($cftype eq 'Image') {
- push @values,
- \($m->scomp( '/Elements/ShowCustomFieldImage',
- Object => $v ));
- } elsif ( $cftype eq 'Date' or $cftype eq 'DateTime' ) {
- # then actually return the date object;
- # ProcessColumnMapValue will stringify it
- my $DateObj = RT::Date->new( $session{'CurrentUser'} );
- $DateObj->Set(Format => 'unknown', Value => $v->Content);
- push @values, $DateObj;
- } else {
- push @values, $v->Content;
+ return $cf;
+ },
+ render => sub {
+ my ($cf, $ocfvs) = @_;
+ my $comp = $m->comp_exists("/Elements/ShowCustomField".$cf->Type)
+ ? "/Elements/ShowCustomField".$cf->Type
+ : undef;
+
+ my @values = map {
+ $comp
+ ? \($m->scomp( $comp, Object => $_ ))
+ : $_->Content
+ } @$ocfvs;
+
+ if (@values > 1) {
+ for my $value (splice @values) {
+ push @values, \"<li>", $value, \"</li> \n";
}
- push @values, \'<br />'; # this is deeply silly
- } while ($v = $values->Next);
- pop @values; # Remove that last <br />
+ @values = (\"<ul class='cf-values'>", @values, \"</ul>");
+ }
return @values;
},
},
@@ -146,9 +150,9 @@ my $COLUMN_MAP = {
my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
- onclick="setCheckbox(this.form, },
+ onclick="setCheckbox(this, },
$m->interp->apply_escapes($name,'j'),
- \qq{, this.checked)" />};
+ \qq{)" />};
},
value => sub {
my $id = $_[0]->id;
@@ -187,20 +191,60 @@ my $COLUMN_MAP = {
} qw(WebPath WebBaseURL WebURL)),
WebRequestPath => { value => sub { substr( $m->request_path, 1 ) } },
WebRequestPathDir => { value => sub { substr( $m->request_comp->dir_path, 1 ) } },
+ WebHomePath => {
+ value => sub {
+ my $path = RT->Config->Get("WebPath");
+ if (not $session{CurrentUser}->Privileged) {
+ $path .= "/SelfService";
+ }
+ return \$path;
+ },
+ },
+ CurrentUser => { value => sub { $session{CurrentUser}->id } },
+ CurrentUserName => { value => sub { $session{CurrentUser}->Name } },
};
$COLUMN_MAP->{'CF'} = $COLUMN_MAP->{'CustomField'};
+Scalar::Util::weaken($WCOLUMN_MAP);
+
+my $ROLE_MAP = {};
+
</%ONCE>
<%INIT>
$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
-$m->callback( COLUMN_MAP => $COLUMN_MAP );
+
+my $generic_with_roles;
+
+# Add in roles
+my $RecordClass = $Class;
+$RecordClass =~ s/_/:/g;
+if ($RecordClass->DOES("RT::Record::Role::Roles")) {
+ unless ($ROLE_MAP->{$RecordClass}) {
+ for my $role ($RecordClass->Roles) {
+ my $attrs = $RecordClass->Role($role);
+ $ROLE_MAP->{$RecordClass}{$role} = {
+ title => $role,
+ attribute => $attrs->{Column} || "$role.EmailAddress",
+ value => sub { return \($m->scomp("/Elements/ShowPrincipal", Object => $_[0]->RoleGroup($role) ) ) },
+ };
+ $ROLE_MAP->{$RecordClass}{$role . "s"} = $ROLE_MAP->{$RecordClass}{$role}
+ unless $attrs->{Single};
+ }
+ }
+ $generic_with_roles = { %{$COLUMN_MAP}, %{$ROLE_MAP->{$RecordClass}} };
+} else {
+ $generic_with_roles = { %{$COLUMN_MAP} };
+}
+
+$m->callback( COLUMN_MAP => $generic_with_roles );
# first deal with class specific things
if (RT::Interface::Web->ComponentPathIsSafe($Class) and $m->comp_exists("/Elements/$Class/ColumnMap")) {
- my $class_map = $m->comp("/Elements/$Class/ColumnMap", Attr => $Attr, Name => $Name );
+ my $class_map = $m->comp("/Elements/$Class/ColumnMap", Attr => $Attr, Name => $Name, GenericMap => $generic_with_roles );
return $class_map if defined $class_map;
}
-return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
+
+return GetColumnMapEntry( Map => $generic_with_roles, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/GnuPG/KeyIssues b/rt/share/html/Elements/Crypt/KeyIssues
index a7ef7c4..35c1264 100644
--- a/rt/share/html/Elements/GnuPG/KeyIssues
+++ b/rt/share/html/Elements/Crypt/KeyIssues
@@ -46,7 +46,8 @@
%#
%# END BPS TAGGED BLOCK }}}
% if ( @$Issues || @$SignAddresses ) {
-<&| /Widgets/TitleBox, title => loc('GnuPG issues') &>
+<div class="results">
+<&| /Widgets/TitleBox, title => loc('[_1] issues', RT->Config->Get('Crypt')->{'Outgoing'}) &>
% if ( @$SignAddresses ) {
<% loc("The system is unable to sign outgoing email messages. This usually indicates that the passphrase was mis-set, or that GPG Agent is down. Please alert your system administrator immediately. The problem addresses are:") %>
@@ -58,9 +59,9 @@
% }
% if (@$Issues == 1) {
-<% loc("You are going to encrypt outgoing email messages, but there is a problem with a recipient's public key. You have to fix the problem with the key, disable sending a message to that recipient, or disable encryption.") %>
+<% loc("You are going to encrypt outgoing email messages, but there is a problem with a recipient's public key/certificate. You have to fix the problem with the key/certificate, disable sending a message to that recipient, or disable encryption.") %>
% } elsif (@$Issues > 1) {
-<% loc("You are going to encrypt outgoing email messages, but there are problems with recipients' public keys. You have to fix the problems with the keys, disable sending a message to the recipients with key problems, or disable encryption.") %>
+<% loc("You are going to encrypt outgoing email messages, but there are problems with recipients' public keys/certificates. You have to fix the problems with the keys/certificates, disable sending a message to the recipients with problems, or disable encryption.") %>
% }
<ul>
@@ -69,12 +70,12 @@
% if ( $issue->{'User'} ) {
User <a href="<% RT->Config->Get('WebPath') %>/Admin/Users/Modify.html?id=<% $issue->{'User'}->id %>"><&/Elements/ShowUser, User => $issue->{'User'} &></a> has a problem.
% } else {
-There is a problem with key(s) for address <% $issue->{'EmailAddress'} %>, but there is no user in the DB for this address.
+There is a problem with key/certificate(s) for address <% $issue->{'EmailAddress'} %>, but there is no user in the DB for this address.
% }
<% $issue->{'Message'} %>
<br />
-Select a key you want to use for encryption:
-<& /Elements/GnuPG/SelectKeyForEncryption,
+Select a key/certificate you want to use for encryption:
+<& /Elements/Crypt/SelectKeyForEncryption,
Name => 'UseKey-'. $issue->{'EmailAddress'},
EmailAddress => $issue->{'EmailAddress'},
Default => ( $issue->{'User'}? $issue->{'User'}->PreferredKey : undef ),
@@ -83,6 +84,7 @@ Select a key you want to use for encryption:
% }
</ul>
</&>
+</div>
% }
<%ARGS>
diff --git a/rt/share/html/Elements/GnuPG/SelectKeyForEncryption b/rt/share/html/Elements/Crypt/SelectKeyForEncryption
index 54aa3c6..2bf79bc 100644
--- a/rt/share/html/Elements/GnuPG/SelectKeyForEncryption
+++ b/rt/share/html/Elements/Crypt/SelectKeyForEncryption
@@ -50,16 +50,15 @@
% } else {
<select name="<% $Name %>">
% foreach my $key (@keys) {
-<option value="<% $key->{'Fingerprint'} %>"><% $key->{'Fingerprint'} %> <% loc("(trust: [_1])", $key->{'TrustTerse'}) %></option>
+<option value="<% $key->{'Fingerprint'} %>"><% $key->{'Formatted'} %> <% loc("(trust: [_1])", $key->{'TrustTerse'}) %></option>
% }
</select>
% }
<%INIT>
-require RT::Crypt::GnuPG;
my $d;
-my %res = RT::Crypt::GnuPG::GetKeysForEncryption($EmailAddress);
+my %res = RT::Crypt->GetKeysForEncryption($EmailAddress);
# move the preferred key to the top of the list
my @keys = map {
$_->{'Fingerprint'} eq ( $Default || '' )
diff --git a/rt/share/html/Elements/GnuPG/SelectKeyForSigning b/rt/share/html/Elements/Crypt/SelectKeyForSigning
index 047d558..bbd9bda 100644
--- a/rt/share/html/Elements/GnuPG/SelectKeyForSigning
+++ b/rt/share/html/Elements/Crypt/SelectKeyForSigning
@@ -58,7 +58,10 @@ $Name => 'SignUsing',
$User => undef,
</%ARGS>
<%INIT>
-return unless RT->Config->Get('GnuPG')->{'Enable'};
+return unless RT->Config->Get('Crypt')->{'Enable'};
-my $user_key = $User->PrivateKey;
+# XXX: Only GnuPG at this moment supports user's private keys
+my $user_key;
+$user_key = $User->PrivateKey
+ if RT->Config->Get('Crypt')->{'Outgoing'} eq 'GnuPG';
</%INIT>
diff --git a/rt/share/html/Elements/GnuPG/SignEncryptWidget b/rt/share/html/Elements/Crypt/SignEncryptWidget
index c1b5b25..33136f9 100644
--- a/rt/share/html/Elements/GnuPG/SignEncryptWidget
+++ b/rt/share/html/Elements/Crypt/SignEncryptWidget
@@ -47,6 +47,7 @@
%# END BPS TAGGED BLOCK }}}
<table><tr>
% my $columnsplit = "</td><td>";
+% if ( RT->Config->Get('Crypt')->{'Outgoing'} eq 'GnuPG' ) {
<td><% loc( 'Sign[_1][_2] using [_3]',
$columnsplit,
$m->scomp('/Widgets/Form/Boolean:InputOnly',
@@ -54,6 +55,14 @@
),
$m->scomp('SelectKeyForSigning', User => $session{'CurrentUser'}->UserObj ),
) |n %></td>
+% } else {
+<td><% loc( 'Sign[_1][_2]',
+ $columnsplit,
+ $m->scomp('/Widgets/Form/Boolean:InputOnly',
+ Name => 'Sign', CurrentValue => $self->{'Sign'}
+ ),
+) |n %></td>
+% }
<td><% loc('Encrypt')%></td>
<td><& /Widgets/Form/Boolean:InputOnly, Name => 'Encrypt', CurrentValue => $self->{'Encrypt'} &></td>
@@ -70,9 +79,7 @@ return unless $self;
$Arguments => {}
</%ARGS>
<%INIT>
-return undef unless RT->Config->Get('GnuPG')->{'Enable'};
-
-require RT::Crypt::GnuPG;
+return undef unless RT->Config->Get('Crypt')->{'Enable'};
return { %$Arguments };
</%INIT>
</%METHOD>
@@ -84,7 +91,7 @@ $self => undef,
<%INIT>
return unless $self;
-return $m->comp( '/Elements/GnuPG/KeyIssues',
+return $m->comp( '/Elements/Crypt/KeyIssues',
Issues => $self->{'GnuPGRecipientsKeyIssues'} || [],
SignAddresses => $self->{'GnuPGCanNotSignAs'} || [],
);
@@ -138,11 +145,11 @@ if ( $self->{'Sign'} ) {
if ($address ne $private and $address ne $queue) {
push @{ $self->{'GnuPGCanNotSignAs'} ||= [] }, $address;
$checks_failure = 1;
- } elsif ( not RT::Crypt::GnuPG::DrySign( $address ) ) {
+ } elsif ( not RT::Crypt->DrySign( Signer => $address ) ) {
push @{ $self->{'GnuPGCanNotSignAs'} ||= [] }, $address;
$checks_failure = 1;
} else {
- RT::Crypt::GnuPG::UseKeyForSigning( $self->{'SignUsing'} )
+ RT::Crypt->UseKeyForSigning( $self->{'SignUsing'} )
if $self->{'SignUsing'};
}
}
@@ -165,13 +172,13 @@ if ( $self->{'Encrypt'} ) {
my %seen;
@recipients = grep !$seen{ lc $_ }++, @recipients;
- RT::Crypt::GnuPG::UseKeyForEncryption(
+ RT::Crypt->UseKeyForEncryption(
map { (/^UseKey-(.*)$/)[0] => $self->{ $_ } }
grep $self->{ $_ } && /^UseKey-/,
keys %$self
);
- my ($status, @issues) = RT::Crypt::GnuPG::CheckRecipients( @recipients );
+ my ($status, @issues) = RT::Crypt->CheckRecipients( @recipients );
push @{ $self->{'GnuPGRecipientsKeyIssues'} ||= [] }, @issues;
$checks_failure = 1 unless $status;
}
diff --git a/rt/share/html/Elements/CryptStatus b/rt/share/html/Elements/CryptStatus
new file mode 100644
index 0000000..b022b10
--- /dev/null
+++ b/rt/share/html/Elements/CryptStatus
@@ -0,0 +1,195 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Message
+$WarnUnsigned => undef
+$Reverify => 1
+</%ARGS>
+<%INIT>
+my @runs;
+my $needs_unsigned_warning = $WarnUnsigned;
+
+my @protocols = RT::Crypt->EnabledProtocols;
+my $re_protocols = join '|', map "\Q$_\E", @protocols;
+
+foreach ( $Message->SplitHeaders ) {
+ if ( s/^X-RT-($re_protocols)-Status:\s*//io ) {
+ push @runs, [ $1, RT::Crypt->ParseStatus( Protocol => "$1", Status => $_ ) ];
+ }
+
+ $needs_unsigned_warning = 0 if /^X-RT-Incoming-Signature:/;
+
+ # if this is not set, then the email is generated by RT, and so we don't
+ # need "email is unsigned" warnings
+ $needs_unsigned_warning = 0 if not /^Received:/;
+}
+
+return unless @runs or $needs_unsigned_warning;
+
+my $reverify_cb = sub {
+ my $top = shift;
+
+ my $txn = $top->TransactionObj;
+ unless ( $txn && $txn->id ) {
+ return (0, "Couldn't get transaction of attachment #". $top->id);
+ }
+
+ my $attachments = $txn->Attachments->Clone;
+ $attachments->Limit( FIELD => 'ContentType', VALUE => 'application/x-rt-original-message' );
+ my $original = $attachments->First;
+ unless ( $original ) {
+ return (0, "Couldn't find attachment with original email of transaction #". $txn->id);
+ }
+
+ my $parser = RT::EmailParser->new();
+ $parser->SmartParseMIMEEntityFromScalar(
+ Message => $original->Content,
+ Decode => 0,
+ Exact => 1,
+ );
+ my $entity = $parser->Entity;
+ unless ( $entity ) {
+ return (0, "Couldn't parse content of attachment #". $original->id);
+ }
+
+ my @res = RT::Crypt->VerifyDecrypt( Entity => $entity );
+ return (0, "Content of attachment #". $original->id ." is not signed and/or encrypted")
+ unless @res;
+
+ $top->DelHeader("X-RT-$_-Status") for RT::Crypt->Protocols;
+ $top->AddHeader(map { ("X-RT-". $_->{Protocol} ."-Status" => $_->{'status'} ) } @res);
+ $top->DelHeader("X-RT-Privacy");
+ my %protocols; $protocols{$_->{Protocol}}++ for @res;
+ $top->AddHeader('X-RT-Privacy' => $_ ) for sort keys %protocols;
+
+ $top->DelHeader('X-RT-Incoming-Signature');
+ my @status = RT::Crypt->ParseStatus(
+ Protocol => $res[0]{'Protocol'},
+ Status => $res[0]{'status'},
+ );
+ for ( @status ) {
+ if ( $_->{'Operation'} eq 'Verify' && $_->{'Status'} eq 'DONE' ) {
+ $top->AddHeader( 'X-RT-Incoming-Signature' => $_->{'UserString'} );
+ $needs_unsigned_warning = 0;
+ }
+ }
+ return (1, "Reverified original message");
+};
+
+my @messages;
+foreach my $run ( @runs ) {
+ my $protocol = shift @$run;
+ $protocol = $RT::Crypt::PROTOCOLS{lc $protocol};
+ foreach my $line ( @$run ) {
+ if ( $line->{'Operation'} eq 'KeyCheck' ) {
+ next unless $Reverify;
+ # if a public key was missing during verification then we want try again
+ next unless $line->{'KeyType'} eq 'public' && $line->{'Status'} eq 'MISSING';
+
+ # but only if we have key
+ my %key = RT::Crypt->GetPublicKeyInfo(
+ Protocol => $protocol, Key => $line->{'Key'}
+ );
+ if ( $key{'info'} ) {
+ my ($status, $msg) = $reverify_cb->($Message);
+ unless ($status) {
+ $RT::Logger->error($msg);
+ } else {
+ return $m->comp('SELF', %ARGS, Reverify => 0);
+ }
+ }
+ else {
+ push @messages, {
+ Tag => $protocol,
+ Classes => [qw/keycheck bad/],
+ Value => loc( "Public key '0x[_1]' is required to verify signature", $line->{'Key'} ),
+ };
+ }
+ }
+ elsif ( $line->{'Operation'} eq 'PassphraseCheck' ) {
+ next if $line->{'Status'} eq 'DONE';
+ push @messages, {
+ Tag => $protocol,
+ Classes => ['passphrasecheck', lc $line->{Status}],
+ Value => loc( $line->{'Message'} ),
+ };
+ }
+ elsif ( $line->{'Operation'} eq 'Decrypt' ) {
+ push @messages, {
+ Tag => $protocol,
+ Classes => ['decrypt', lc $line->{Status}],
+ Value => loc( $line->{'Message'} ),
+ };
+ }
+ elsif ( $line->{'Operation'} eq 'Verify' ) {
+ push @messages, {
+ Tag => $protocol,
+ Classes => ['verify', lc $line->{Status}, 'trust-'.($line->{Trust} || 'UNKNOWN')],
+ Value => loc( $line->{'Message'} ),
+ };
+ }
+ else {
+ next if $line->{'Status'} eq 'DONE';
+ push @messages, {
+ Tag => $protocol,
+ Classes => [lc $line->{Operation}, lc $line->{Status}],
+ Value => loc( $line->{'Message'} ),
+ }
+ }
+ }
+}
+
+push @messages, { Tag => "Signing", Classes => ['verify', 'bad'], Value => loc('Warning! This is NOT signed!') }
+ if $needs_unsigned_warning;
+return unless @messages;
+
+my %seen;
+@messages = grep !$seen{$_->{Value}}++, @messages;
+
+return @messages;
+</%INIT>
diff --git a/rt/share/html/Elements/EditCustomField b/rt/share/html/Elements/EditCustomField
index d0928dc..db6d181 100644
--- a/rt/share/html/Elements/EditCustomField
+++ b/rt/share/html/Elements/EditCustomField
@@ -55,25 +55,39 @@ unless ( $Type ) {
}
my $Values;
-if ( $Object && $Object->id ) {
- $NamePrefix ||= join '-',
- 'Object', ref($Object), $Object->Id, 'CustomField', '';
+if ( $Object ) {
+ $Grouping =~ s/\W//g if $Grouping;
- $Values = $Object->CustomFieldValues( $CustomField->id );
- $Values->Columns(
- qw( id CustomField ObjectType ObjectId Disabled Content
- ContentType ContentEncoding SortOrder Creator Created
- LastUpdatedBy LastUpdated )
- );
- # Don't take care of $Values if there isn't values inside
- undef ( $Values ) unless ( $Values->Count );
+ if ( $Object->Id ) {
+ $Values = $Object->CustomFieldValues( $CustomField->id );
+ $Values->Columns(
+ qw( id CustomField ObjectType ObjectId Disabled Content
+ ContentType ContentEncoding SortOrder Creator Created
+ LastUpdatedBy LastUpdated )
+ );
+ # Don't take care of $Values if there isn't values inside
+ undef ( $Values ) unless ( $Values->Count );
+ }
+}
+
+my $Name;
+if ( !$NamePrefix ) {
+ $Name = GetCustomFieldInputName(Object => $Object, CustomField => $CustomField, Grouping => $Grouping );
}
# Always fill $Default with submited values if it's empty
if ( ( !defined $Default || !length $Default ) && $DefaultsFromTopArguments ) {
my %TOP = %$DECODED_ARGS;
- $Default = $TOP{ $NamePrefix .$CustomField->Id . '-Values' }
- || $TOP{ $NamePrefix .$CustomField->Id . '-Value' };
+ $Default = $TOP{ $Name } if $Name;
+ # check both -Values and -Value for back compatibility
+ if ( $NamePrefix ) {
+ $Default //= $TOP{ $NamePrefix . $CustomField->Id . '-Values' }
+ // $TOP{ $NamePrefix . $CustomField->Id . '-Value' };
+ }
+ else {
+ my $prefix = GetCustomFieldInputNamePrefix(Object => $Object, CustomField => $CustomField, Grouping => $Grouping );
+ $Default //= $TOP{ $prefix . 'Values' } // $TOP{ $prefix . 'Value' };
+ }
}
my $MaxValues = $CustomField->MaxValues;
@@ -85,10 +99,13 @@ if ($MaxValues == 1 && $Values) {
}
# The "Magic" hidden input causes RT to know that we were trying to edit the field, even if
# we don't see a value later, since browsers aren't compelled to submit empty form fields
-$m->out("\n".'<input type="hidden" class="hidden" name="'.$m->interp->apply_escapes($NamePrefix, 'h').$CustomField->Id.'-Values-Magic" value="1" />'."\n");
+$m->out("\n".'<input type="hidden" class="hidden" name="'
+ . ($Name ? $m->interp->apply_escapes($Name, 'h') : $m->interp->apply_escapes($NamePrefix, 'h').$CustomField->Id.'-Values')
+ . '-Magic" value="1" />'."\n");
+
my $EditComponent = "EditCustomField$Type";
-$m->callback( %ARGS, CallbackName => 'EditComponentName', Name => \$EditComponent, CustomField => $CustomField, Object => $Object );
+$m->callback( %ARGS, CallbackName => 'EditComponentName', Name => \$EditComponent, CustomField => $CustomField, Object => $Object, Rows => \$Rows, Cols => \$Cols);
$EditComponent = "EditCustomField$Type" unless $m->comp_exists($EditComponent);
return $m->comp(
@@ -103,9 +120,12 @@ return $m->comp(
Multiple => ($MaxValues != 1),
NamePrefix => $NamePrefix,
CustomField => $CustomField,
+ Name => $Name,
+ $CustomField->BasedOn && $Name ? ( BasedOnName => GetCustomFieldInputName(Object => $Object, CustomField => $CustomField->BasedOnObj, Grouping => $Grouping) ) : (),
);
</%INIT>
<%ARGS>
+$Grouping => undef
$Object => undef
$CustomField => undef
$NamePrefix => undef
diff --git a/rt/share/html/Elements/EditCustomFieldAutocomplete b/rt/share/html/Elements/EditCustomFieldAutocomplete
index 1dd4b62..092f835 100644
--- a/rt/share/html/Elements/EditCustomFieldAutocomplete
+++ b/rt/share/html/Elements/EditCustomFieldAutocomplete
@@ -53,13 +53,13 @@ cols="<% $Cols %>" \
% if ( defined $Rows ) {
rows="<% $Rows %>" \
% }
-name="<% $name %>-Values" id="<% $name %>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
+name="<% $name %>" id="<% $name %>" class="CF-<%$CustomField->id%>-Edit"><% $Default || '' %></textarea>
<script type="text/javascript">
-var id = <% "$name-Values" |n,j%>;
+var id = <% "$name" |n,j%>;
id = id.replace(/:/g,'\\:');
jQuery('#'+id).autocomplete( {
- source: <%RT->Config->Get('WebPath') |n,j%>+"/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% "$name-Values" |n,u,j%>,
+ source: RT.Config.WebHomePath + "/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% $name |n,u,j%>,
focus: function () {
// prevent value inserted on focus
return false;
@@ -78,18 +78,18 @@ jQuery('#'+id).autocomplete( {
}
);
% } else {
-<input type="text" id="<% $name %>-Value" name="<% $name %>-Value" class="CF-<%$CustomField->id%>-Edit" value="<% $Default || '' %>"/>
+<input type="text" id="<% $name %>" name="<% $name %>" class="CF-<%$CustomField->id%>-Edit" value="<% $Default || '' %>"/>
<script type="text/javascript">
-var id = <% "$name-Value" |n,j%>;
+var id = <% $name |n,j%>;
id = id.replace(/:/g,'\\:');
jQuery('#'+id).autocomplete( {
- source: <%RT->Config->Get('WebPath')|n,j%>+"/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% "$name-Value" |n,u,j%>
+ source: RT.Config.WebHomePath + "/Helpers/Autocomplete/CustomFieldValues?"+<% $Context |n,j %>+<% $name |n,u,j%>
}
);
% }
</script>
<%INIT>
-my $name = $NamePrefix . $CustomField->Id;
+my $name = $Name || $NamePrefix . $CustomField->Id . ( $Multiple ? '-Values' : '-Value' );
if ( $Default && !$Multiple ) {
$Default =~ s/\s*\r*\n\s*/ /g;
}
@@ -108,6 +108,7 @@ if ($CustomField->ContextObject) {
<%ARGS>
$CustomField => undef
$NamePrefix => undef
+$Name => undef
$Default => undef
$Values => undef
$Multiple => undef
diff --git a/rt/share/html/Elements/EditCustomFieldBinary b/rt/share/html/Elements/EditCustomFieldBinary
index 7a2de49..fc6ee3f 100644
--- a/rt/share/html/Elements/EditCustomFieldBinary
+++ b/rt/share/html/Elements/EditCustomFieldBinary
@@ -47,15 +47,26 @@
%# END BPS TAGGED BLOCK }}}
% while ( $Values and my $value = $Values->Next ) {
%# XXX - let user download the file(s) here?
-<input type="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" class="checkbox CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><a href="<%RT->Config->Get('WebPath')%>/Download/CustomFieldValue/<% $value->Id %>/<% $value->Content |un %>"><% $value->Content %></a><br />
+<input type="checkbox" name="<%$delete_name%>" class="checkbox CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><a href="<%RT->Config->Get('WebPath')%>/Download/CustomFieldValue/<% $value->Id %>/<% $value->Content |un %>"><% $value->Content %></a><br />
% }
-% if (!$MaxValues || !$Values || $Values->Count < $MaxValues) {
-<input type="file" name="<% $NamePrefix %><% $CustomField->Id %>-Upload" class="CF-<%$CustomField->id%>-Edit" />
+% if ($MaxValues && $Values && $Values->Count >= $MaxValues ) {
+<div class="hints">
+<&|/l&>Reached maximum number, so new values will override old ones.</&>
+</div>
% }
+<input type="file" name="<% $name %>" class="CF-<%$CustomField->id%>-Edit" />
+
+<%INIT>
+my $name = $Name || $NamePrefix . $CustomField->Id . '-Upload';
+my $delete_name = $name;
+$delete_name =~ s!-Upload$!-DeleteValueIds!;
+</%INIT>
+
<%ARGS>
$Object => undef
$CustomField => undef
$NamePrefix => undef
+$Name => undef
$Default => undef
$Values => undef
$MaxValues => undef
diff --git a/rt/share/html/Elements/EditCustomFieldCombobox b/rt/share/html/Elements/EditCustomFieldCombobox
index 718ff30..28fc2a1 100644
--- a/rt/share/html/Elements/EditCustomFieldCombobox
+++ b/rt/share/html/Elements/EditCustomFieldCombobox
@@ -46,17 +46,25 @@
%#
%# END BPS TAGGED BLOCK }}}
% while ($Values and my $value = $Values->Next and $Multiple) {
-<input type="checkbox" class="checkbox CF-<%$CustomField->id%>-Edit" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" class="CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><% $value->Content %>
+<input type="checkbox" id="<%$delete_name%>" class="checkbox CF-<%$CustomField->id%>-Edit" name="<%$delete_name%>" class="CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" />
+<label for="<%$delete_name%>"><% $value->Content %></label>
<br />
% }
% (!$Multiple or !$MaxValues or !$Values or $Values->Count < $MaxValues) or return;
<& /Widgets/ComboBox,
- Name => $NamePrefix . $CustomField->Id . "-Value",
+ Name => $name,
Default => $Default,
Rows => $Rows,
Class => "CF-".$CustomField->id."-Edit",
Values => [map {$_->Name} @{$CustomField->Values->ItemsArrayRef}],
&>
+
+<%INIT>
+my $name = $Name || $NamePrefix . $CustomField->Id . '-Value';
+my $delete_name = $name;
+$delete_name =~ s!-Value$!-DeleteValueIds!;
+</%INIT>
+
<%ARGS>
$Object => undef
$CustomField => undef
@@ -66,4 +74,5 @@ $Values => undef
$Multiple => 0
$Rows => undef
$MaxValues => undef
+$Name => undef
</%ARGS>
diff --git a/rt/share/html/Elements/SelectTicketSortBy b/rt/share/html/Elements/EditCustomFieldCustomGroupings
index d06692a..0287b18 100755..100644
--- a/rt/share/html/Elements/SelectTicketSortBy
+++ b/rt/share/html/Elements/EditCustomFieldCustomGroupings
@@ -45,18 +45,29 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<select name="<%$Name%>">
-% foreach my $field (@sortfields) {
-<option value="<%$field%>"<% $field eq $Default && qq[ selected="selected"] |n%>><% loc($field) %></option>
+% foreach my $group ( @Groupings ) {
+<&| /Widgets/TitleBox,
+ title => $group? loc($group) : loc('Custom Fields'),
+ class => $css_class .' '. ($group? CSSClass("$css_class-$group") : ''),
+ id => ($group ? CSSClass("$css_class-$group") : $css_class),
+ hide_empty => 1,
+ %$TitleBoxARGS,
+&>
+% $ARGS{CustomFields} = $CustomFieldGenerator->() if $CustomFieldGenerator;
+<& EditCustomFields, %ARGS, Object => $Object, Grouping => $group &>
+</&>
% }
-</select>
-
+<%ARGS>
+$Object
+$CustomFieldGenerator => undef,
+@Groupings => (RT::CustomField->CustomGroupings( $Object ), '')
+</%ARGS>
<%INIT>
-my $tickets = RT::Tickets->new($session{'CurrentUser'});
-my @sortfields = $tickets->SortFields();
+my $css_class = lc(ref($Object)||$Object);
+$css_class =~ s/^rt:://;
+$css_class =~ s/::/-/g;
+$css_class = CSSClass($css_class);
+$css_class .= '-info-cfs';
+my $TitleBoxARGS = delete $ARGS{TitleBoxARGS} || {};
</%INIT>
-<%ARGS>
-$Name => 'SortTicketsBy'
-$Default => 'id'
-</%ARGS>
diff --git a/rt/share/html/Elements/EditCustomFieldDate b/rt/share/html/Elements/EditCustomFieldDate
index 25e01f4..6568d34 100644
--- a/rt/share/html/Elements/EditCustomFieldDate
+++ b/rt/share/html/Elements/EditCustomFieldDate
@@ -45,7 +45,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% my $name = $NamePrefix.$CustomField->Id.'-Values';
+% my $name = $Name || $NamePrefix.$CustomField->Id.'-Values';
<& /Elements/SelectDate, Name => "$name", current => 0, ShowTime => 0, $KeepValue && $Default ? (Default => $Default) : () &> (<%$DateObj->AsString(Time => 0, Timezone => 'utc')%>)
<%INIT>
@@ -59,5 +59,6 @@ $NamePrefix => undef
$Default => undef
$Values => undef
$MaxValues => 1
+$Name => undef
$KeepValue => undef
</%ARGS>
diff --git a/rt/share/html/Elements/EditCustomFieldDateTime b/rt/share/html/Elements/EditCustomFieldDateTime
index 28fecd3..0ca7bb2 100644
--- a/rt/share/html/Elements/EditCustomFieldDateTime
+++ b/rt/share/html/Elements/EditCustomFieldDateTime
@@ -45,7 +45,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% my $name = $NamePrefix.$CustomField->Id.'-Values';
+% my $name = $Name || $NamePrefix.$CustomField->Id.'-Values';
<& /Elements/SelectDate, Name => "$name", current => 0, $KeepValue && $Default ? (Default => $Default) : () &> (<%$DateObj->AsString($KeepValue ? ( Timezone => 'utc' ) : () )%>)
<%INIT>
@@ -59,6 +59,7 @@ $NamePrefix => undef
$Default => undef
$Values => undef
$MaxValues => 1
+$Name => undef
$Format => 'ISO'
$KeepValue => undef
</%ARGS>
diff --git a/rt/share/html/Elements/EditCustomFieldFreeform b/rt/share/html/Elements/EditCustomFieldFreeform
index 3514b1d..3c2847c 100644
--- a/rt/share/html/Elements/EditCustomFieldFreeform
+++ b/rt/share/html/Elements/EditCustomFieldFreeform
@@ -45,7 +45,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% my $name = $NamePrefix . $CustomField->Id . '-Value';
+% my $name = $Name || $NamePrefix . $CustomField->Id . ( $Multiple ? '-Values' : '-Value' );
% if ($Multiple) {
<textarea \
% if ( defined $Cols ) {
@@ -54,7 +54,7 @@ cols="<% $Cols %>" \
% if ( defined $Rows ) {
rows="<% $Rows %>" \
% }
-name="<%$name%>s" id="<%$name%>s" wrap="off" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
+name="<%$name%>" id="<%$name%>" wrap="off" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
% } else {
<input type="text" name="<%$name%>" id="<%$name%>" \
% if ( defined $Cols ) {
@@ -74,6 +74,7 @@ unless ( $Multiple ) {
$Object => undef
$CustomField => undef
$NamePrefix => undef
+$Name => undef
$Default => undef
$Values => undef
$Multiple => undef
diff --git a/rt/share/html/Elements/EditCustomFieldImage b/rt/share/html/Elements/EditCustomFieldImage
index 96e0ac6..0b2340a 100644
--- a/rt/share/html/Elements/EditCustomFieldImage
+++ b/rt/share/html/Elements/EditCustomFieldImage
@@ -46,16 +46,27 @@
%#
%# END BPS TAGGED BLOCK }}}
% while ($Values and my $value = $Values->Next ) {
-<input type="checkbox" class="checkbox" name="<%$NamePrefix%><%$CustomField->Id%>-DeleteValueIds" class="CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><& ShowCustomFieldImage, Object => $value &>
+<input type="checkbox" class="checkbox" name="<%$delete_name%>" class="CF-<%$CustomField->id%>-Edit" value="<% $value->Id %>" /><& ShowCustomFieldImage, Object => $value &>
<br />
% }
-% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
-<input type="file" name="<%$NamePrefix%><%$CustomField->Id%>-Upload" class="CF-<%$CustomField->id%>-Edit" />
+% if ($MaxValues && $Values && $Values->Count >= $MaxValues ) {
+<div class="hints">
+<&|/l&>Reached maximum number, so new values will override old ones.</&>
+</div>
% }
+<input type="file" name="<%$name%>" class="CF-<%$CustomField->id%>-Edit" />
+
+<%INIT>
+my $name = $Name || $NamePrefix . $CustomField->Id . '-Upload';
+my $delete_name = $name;
+$delete_name =~ s!-Upload$!-DeleteValueIds!;
+</%INIT>
+
<%ARGS>
$Object => undef
$CustomField => undef
$NamePrefix => undef
+$Name => undef
$Default => undef
$Values => undef
$MaxValues => undef
diff --git a/rt/share/html/Elements/EditCustomFieldSelect b/rt/share/html/Elements/EditCustomFieldSelect
index 662c188..9fcebfb 100644
--- a/rt/share/html/Elements/EditCustomFieldSelect
+++ b/rt/share/html/Elements/EditCustomFieldSelect
@@ -50,12 +50,10 @@
%# (perhaps by tweaking the .display style?)
% my $selected = 0;
% my @category;
-% my $id = $NamePrefix . $CustomField->Id;
% my $out = $m->scomp('SELF:options', %ARGS, SelectedRef => \$selected, CategoryRef => \@category);
% if (!$HideCategory and @category and not $CustomField->BasedOnObj->id) {
- <script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/cascaded.js"></script>
%# XXX - Hide this select from w3m?
- <select onchange="filter_cascade(<% "$id-Values" |n,j%>, this.value)" name="<% $id %>-Category" class="CF-<%$CustomField->id%>-Edit">
+ <select onchange="filter_cascade_by_id(<% $name |n,j %>, this.value)" name="<% $name %>-Category" class="CF-<%$CustomField->id%>-Edit">
<option value=""<% !$selected && qq[ selected="selected"] |n %>><&|/l&>-</&></option>
% foreach my $cat (@category) {
% my ($depth, $name) = @$cat;
@@ -63,10 +61,10 @@
% }
</select><br />
% } elsif ($CustomField->BasedOnObj->id) {
-<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/cascaded.js"></script>
+
<script type="text/javascript"><!--
jQuery( function () {
- var basedon = jQuery('[name^=<% $NamePrefix . $CustomField->BasedOnObj->id %>-Value][type!=hidden]:input:not(.hidden)');
+ var basedon = jQuery('[name^="'+<% $BasedOnName || $NamePrefix . $CustomField->BasedOnObj->id . '-Value' |n,j %>+'"][type!="hidden"]:input:not(.hidden)');
basedon.each( function() {
var oldchange = jQuery(this).onchange;
jQuery(this).change( function () {
@@ -82,10 +80,10 @@ jQuery( function () {
}
});
}
- filter_cascade(
- <% "$id-Values" |n,j%>,
+ filter_cascade_by_id(
+ <% $name |n,j%>,
vals,
- 1
+ true
);
if (oldchange != null)
oldchange();
@@ -104,7 +102,7 @@ jQuery( function () {
% if ( $RenderType eq 'List' ) {
<fieldset class="cfedit">
-<div data-name="<%$id%>-Values" id="<%$id%>-Values">
+<div data-name="<%$name%>" id="<%$name%>">
% if ( $checktype eq 'radio' ) {
<div class="none">
<input class="none" type="<% $checktype %>" name="<% $name %>" id="<% $name %>-none" value="" <% keys %default ? '' : ' checked="checked"' |n%> />
@@ -125,14 +123,14 @@ jQuery( function () {
% } else {
% if (@category) {
%# this hidden select is to supply a full list of values,
-%# see filter_cascade() in js/cascaded.js
- <select name="<%$id%>-Values-Complete" id="<%$id%>-Values-Complete" class="hidden" disabled="disabled">
+%# see filter_cascade_select() in js/cascaded.js
+ <select name="<%$name%>-Complete" id="<%$name%>-Complete" class="hidden" disabled="disabled">
<option value=""<% !$selected && qq[ selected="selected"] |n %>><&|/l&>(no value)</&></option>
% $m->out($out);
</select>
% }
<select
- name="<%$id%>-Values" id="<%$id%>-Values" class="CF-<%$CustomField->id%>-Edit"
+ name="<%$name%>" id="<%$name%>" class="CF-<%$CustomField->id%>-Edit"
% if ( $Rows && ( $Multiple || !@category || $RenderType eq 'Select box') ) {
size="<% $Rows %>"
% }
@@ -149,12 +147,11 @@ if ( $RenderType eq 'Dropdown' ) {
$Rows = 0;
}
-# The following is for rendering checkboxes / radio buttons only
my ($checktype, $name);
-if ( $MaxValues == 1 ) {
- ($checktype, $name) = ('radio', $NamePrefix . $CustomField->Id . '-Value');
+if ( $MaxValues == 1 and $RenderType eq 'List' ) {
+ ($checktype, $name) = ('radio', $Name || $NamePrefix . $CustomField->Id . '-Value');
} else {
- ($checktype, $name) = ('checkbox', $NamePrefix . $CustomField->Id . '-Values');
+ ($checktype, $name) = ('checkbox', $Name || $NamePrefix . $CustomField->Id . '-Values');
}
@Default = grep defined && length, @Default;
@@ -167,6 +164,8 @@ my %default = map {lc $_ => 1} @Default;
$Object => undef
$CustomField => undef
$NamePrefix => undef
+$Name => undef
+$BasedOnName => undef
@Default => ()
$Values => undef
$Multiple => 0
diff --git a/rt/share/html/Elements/EditCustomFieldText b/rt/share/html/Elements/EditCustomFieldText
index a5c043b..35be970 100644
--- a/rt/share/html/Elements/EditCustomFieldText
+++ b/rt/share/html/Elements/EditCustomFieldText
@@ -53,7 +53,7 @@ cols="<% $Cols %>" \
% if ( defined $Rows ) {
rows="<% $Rows %>" \
% }
-name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
+name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
% }
% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
<textarea \
@@ -63,16 +63,18 @@ cols="<% $Cols %>" \
% if ( defined $Rows ) {
rows="<% $Rows %>" \
% }
-name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
+name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% defined($Default) ? $Default : '' %></textarea>
% }
<%INIT>
# XXX - MultiValue textarea is for now outlawed.
$MaxValues = 1;
+my $name = $Name || $NamePrefix . $CustomField->Id . '-Values';
</%INIT>
<%ARGS>
$Object => undef
$CustomField => undef
$NamePrefix => ''
+$Name => undef
$Default => undef
$Values => undef
$MaxValues => undef
diff --git a/rt/share/html/Elements/EditCustomFieldWikitext b/rt/share/html/Elements/EditCustomFieldWikitext
index b75fc99..7acad37 100644
--- a/rt/share/html/Elements/EditCustomFieldWikitext
+++ b/rt/share/html/Elements/EditCustomFieldWikitext
@@ -53,7 +53,7 @@ cols="<% $Cols %>" \
% if ( defined $Rows ) {
rows="<% $Rows %>" \
% }
-name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
+name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% $value->Content %></textarea><br />
% }
% if (!$MaxValues or !$Values or $Values->Count < $MaxValues) {
<textarea \
@@ -63,16 +63,18 @@ cols="<% $Cols %>" \
% if ( defined $Rows ) {
rows="<% $Rows %>" \
% }
-name="<%$NamePrefix%><%$CustomField->Id%>-Values" class="CF-<%$CustomField->id%>-Edit"><% $Default %></textarea>
+name="<%$name%>" class="CF-<%$CustomField->id%>-Edit"><% $Default %></textarea>
% }
<%INIT>
# XXX - MultiValue textarea is for now outlawed.
$MaxValues = 1;
+my $name = $Name || $NamePrefix . $CustomField->Id . '-Values';
</%INIT>
<%ARGS>
$Object => undef
$CustomField => undef
$NamePrefix => undef
+$Name => undef
$Default => undef
$Values => undef
$MaxValues => undef
diff --git a/rt/share/html/Elements/EditCustomFields b/rt/share/html/Elements/EditCustomFields
new file mode 100644
index 0000000..3a38299
--- /dev/null
+++ b/rt/share/html/Elements/EditCustomFields
@@ -0,0 +1,119 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+% $m->callback( CallbackName => 'BeforeCustomFields', Object => $Object,
+% Grouping => $Grouping, ARGSRef => \%ARGS, CustomFields => $CustomFields);
+% if ( $WRAP ) {
+<<% $WRAP %> class="edit-custom-fields">
+% }
+% while ( my $CustomField = $CustomFields->Next ) {
+% next unless $CustomField->CurrentUserHasRight('ModifyCustomField');
+% my $Type = $CustomField->Type || 'Unknown';
+
+ <<% $FIELD %> class="edit-custom-field cftype-<% $Type %>">
+ <<% $CELL %> class="cflabel">
+ <span class="name"><% $CustomField->Name %>:</span><br />
+ <span class="type"><% $CustomField->FriendlyType %></span>
+ </<% $CELL %>>
+ <<% $CELL %> class="entry">
+% my $default = $m->notes('Field-' . $CustomField->Id);
+% $default ||= $ARGS{"CustomField-". $CustomField->Id };
+ <& /Elements/EditCustomField,
+ %ARGS,
+ CustomField => $CustomField,
+ Default => $default,
+ Object => $Object,
+ &>
+% if (my $msg = $m->notes('InvalidField-' . $CustomField->Id)) {
+ <br />
+ <span class="cfinvalidfield"><% $msg %></span>
+% } elsif ($ShowHints and $CustomField->FriendlyPattern) {
+ <br>
+ <span class="cfhints">
+ <&|/l, $CustomField->FriendlyPattern &>Input must match [_1]</&>
+ </span>
+% }
+ </<% $CELL %>>
+% $m->callback( CallbackName => 'AfterCustomFieldValue', CustomField => $CustomField, Object => $Object, Grouping => $Grouping );
+ </<% $FIELD %>>
+% }
+
+% if ( $WRAP ) {
+</<% $WRAP %>>
+% }
+% $m->callback( CallbackName => 'AfterCustomFields', Object => $Object,
+% Grouping => $Grouping, ARGSRef => \%ARGS );
+<%INIT>
+$CustomFields ||= $Object->CustomFields;
+
+$CustomFields->LimitToGrouping( $Object => $Grouping ) if defined $Grouping;
+
+$m->callback( %ARGS, CallbackName => 'MassageCustomFields', CustomFields => $CustomFields );
+
+# don't print anything if there is no custom fields
+return unless $CustomFields->First;
+$CustomFields->GotoFirstItem;
+
+$AsTable ||= $InTable;
+my $FIELD = $AsTable ? 'tr' : 'div';
+my $CELL = $AsTable ? 'td' : 'div';
+my $WRAP = '';
+if ( $AsTable ) {
+ $WRAP = 'table' unless $InTable;
+} else {
+ $WRAP = 'div';
+}
+
+</%INIT>
+<%ARGS>
+$Object
+$CustomFields => undef
+$Grouping => undef
+$AsTable => 1
+$InTable => 0
+$ShowHints => 1
+</%ARGS>
diff --git a/rt/share/html/Elements/EditLinks b/rt/share/html/Elements/EditLinks
index e167d11..2e75936 100755
--- a/rt/share/html/Elements/EditLinks
+++ b/rt/share/html/Elements/EditLinks
@@ -52,59 +52,59 @@
<table>
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
+ <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
<td class="value">
% while (my $link = $Object->DependsOn->Next) {
- <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
- <& ShowLink, URI => $link->TargetURI &><br />
+ <input type="checkbox" class="checkbox" id="DeleteLink--<%$link->Type%>-<%$link->Target%>" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <label for="DeleteLink--<%$link->Type%>-<%$link->Target%>"><& ShowLink, URI => $link->TargetURI &></label><br />
% }
</td>
</tr>
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
+ <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
<td class="value">
% while (my $link = $Object->DependedOnBy->Next) {
- <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
- <& ShowLink, URI => $link->BaseURI &><br />
+ <input type="checkbox" class="checkbox" id="DeleteLink-<%$link->Base%>-<%$link->Type%>-" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <label for="DeleteLink-<%$link->Base%>-<%$link->Type%>-"><& ShowLink, URI => $link->BaseURI &></label><br />
% }
</td>
</tr>
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Parents'), Relation => 'Parents' &>:</td>
+ <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Parents'), Relation => 'Parents' &>:</td>
<td class="value">
% while (my $link = $Object->MemberOf->Next) {
% next if $link->Target and $link->Target =~ m(^freeside://);
- <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
- <& ShowLink, URI => $link->TargetURI &><br />
+ <input type="checkbox" class="checkbox" id="DeleteLink--<%$link->Type%>-<%$link->Target%>" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <label for="DeleteLink--<%$link->Type%>-<%$link->Target%>"><& ShowLink, URI => $link->TargetURI &></label><br />
% }
</td>
</tr>
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Children'), Relation => 'Children' &>:</td>
+ <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Children'), Relation => 'Children' &>:</td>
<td class="value">
% while (my $link = $Object->Members->Next) {
- <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
- <& ShowLink, URI => $link->BaseURI &><br />
+ <input type="checkbox" class="checkbox" id="DeleteLink-<%$link->Base%>-<%$link->Type%>-" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <label for="DeleteLink-<%$link->Base%>-<%$link->Type%>-"><& ShowLink, URI => $link->BaseURI &></label><br />
% }
</td>
</tr>
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
+ <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
<td class="value">
% while (my $link = $Object->RefersTo->Next) {
- <input type="checkbox" class="checkbox" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
- <& ShowLink, URI => $link->TargetURI &><br />
+ <input type="checkbox" class="checkbox" id="DeleteLink--<%$link->Type%>-<%$link->Target%>" name="DeleteLink--<%$link->Type%>-<%$link->Target%>" value="1" />
+ <label for="DeleteLink--<%$link->Type%>-<%$link->Target%>"><& ShowLink, URI => $link->TargetURI &></label><br />
%}
</td>
</tr>
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
+ <td class="labeltop"><& ShowRelationLabel, Object => $Object, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
<td class="value">
% while (my $link = $Object->ReferredToBy->Next) {
% # Skip reminders
% next if (UNIVERSAL::isa($link->BaseObj, 'RT::Ticket') && $link->BaseObj->Type eq 'reminder');
- <input type="checkbox" class="checkbox" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
- <& ShowLink, URI => $link->BaseURI &><br />
+ <input type="checkbox" class="checkbox" id="DeleteLink-<%$link->Base%>-<%$link->Type%>-" name="DeleteLink-<%$link->Base%>-<%$link->Type%>-" value="1" />
+ <label for="DeleteLink-<%$link->Base%>-<%$link->Type%>-"><& ShowLink, URI => $link->BaseURI &></label><br />
% }
</td>
</tr>
@@ -113,68 +113,14 @@
<td><i><&|/l&>(Check box to delete)</&></i></td>
</tr>
</table>
-
+
</td>
<td valign="top">
<h3><&|/l&>New Links</&></h3>
-% if (ref($Object) eq 'RT::Ticket') {
-<i><&|/l&>Enter tickets or URIs to link tickets to. Separate multiple entries with spaces.</&>
-<br /><&|/l&>You may enter links to Articles as "a:###", where ### represents the number of the Article.</&>
-% $m->callback( CallbackName => 'ExtraLinkInstructions' );
-</i><br />
-% } elsif (ref($Object) eq 'RT::Queue') {
-<i><&|/l&>Enter queues or URIs to link queues to. Separate multiple entries with spaces.</&>
-</i><br />
-% } else {
-<i><&|/l&>Enter objects or URIs to link objects to. Separate multiple entries with spaces.</&></i><br />
-% }
-<table>
-% if ($Merge) {
- <tr>
- <td class="label"><&|/l&>Merge into</&>:</td>
- <td class="entry"><input name="<%$id%>-MergeInto" /> <i><&|/l&>(only one ticket)</&></i></td>
- </tr>
-% }
- <tr>
- <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Depends on'), Relation => 'DependsOn' &>:</td>
- <td class="entry"><input name="<%$id%>-DependsOn" /></td>
- </tr>
- <tr>
- <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:</td>
- <td class="entry"><input name="DependsOn-<%$id%>" /></td>
- </tr>
- <tr>
- <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Parents'), Relation => 'Parents' &>:</td>
- <td class="entry"><input name="<%$id%>-MemberOf" /></td>
- </tr>
- <tr>
- <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Children'), Relation => 'Children' &>:</td>
- <td class="entry"> <input name="MemberOf-<%$id%>" /></td>
- </tr>
- <tr>
- <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Refers to'), Relation => 'RefersTo' &>:</td>
- <td class="entry"><input name="<%$id%>-RefersTo" /></td>
- </tr>
- <tr>
- <td class="label"><& ShowRelationLabel, id => $id, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:</td>
- <td class="entry"> <input name="RefersTo-<%$id%>" /></td>
- </tr>
-% $m->callback( CallbackName => 'NewLink' );
-</table>
+<& AddLinks, %ARGS &>
</td>
</tr>
</table>
-
-<%INIT>
-my $id;
-if ($Object && $Object->Id) {
- $id = $Object->Id;
-} else {
- $id = 'new';
-}
-</%INIT>
-
<%ARGS>
$Object => undef
-$Merge => 0
</%ARGS>
diff --git a/rt/share/html/Elements/EditTimeValue b/rt/share/html/Elements/EditTimeValue
index ac27665..e422c0b 100644
--- a/rt/share/html/Elements/EditTimeValue
+++ b/rt/share/html/Elements/EditTimeValue
@@ -46,20 +46,22 @@
%#
%# END BPS TAGGED BLOCK }}}
<input name="<% $ValueName %>" value="<% $Default || '' %>" size="5" />
-<& /Elements/SelectTimeUnits, Name => $UnitName &>
+<& /Elements/SelectTimeUnits, Name => $UnitName, Default => $InUnits &>
<%ARGS>
$Default => ''
$Name => ''
$ValueName => ''
$UnitName => ''
-$InputUnits => 'minutes'
+$InUnits => ''
</%ARGS>
<%INIT>
$ValueName ||= $Name;
$UnitName ||= ($Name||$ValueName) . '-TimeUnits';
+$InUnits ||= $m->request_args->{ $UnitName };
+$InUnits ||= RT->Config->Get('DefaultTimeUnitsToHours', $session{'CurrentUser'}) ? 'hours' : 'minutes';
-if ($InputUnits eq 'minutes' && RT->Config->Get('DefaultTimeUnitsToHours', $session{'CurrentUser'})) {
- $Default = sprintf '%.3f', $Default / 60
- unless $Default eq '';
+if ($Default && $InUnits eq 'hours') {
+ # 0+ here is to remove the ending 0s
+ $Default = 0 + sprintf '%.3f', $Default / 60;
}
</%INIT>
diff --git a/rt/share/html/Elements/EmailInput b/rt/share/html/Elements/EmailInput
index 2ae0ddb..e894a14 100644
--- a/rt/share/html/Elements/EmailInput
+++ b/rt/share/html/Elements/EmailInput
@@ -45,9 +45,11 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<input type="text" id="<% $Name %>" name="<% $Name %>" <% defined $Size ? qq{size="$Size"} : '' |n %> value="<% $Default || '' %>" />
+<input type="text" id="<% $Name %>" name="<% $Name %>" <% defined $Size ? qq{size="$Size"} : '' |n %> value="<% $Default || '' %>" <% $Autocomplete ? q{data-autocomplete="Users"} : '' |n%> <% $AutocompleteMultiple ? q{data-autocomplete-multiple} : '' |n%> />
<%ARGS>
$Name
$Size => 40
$Default => ''
+$Autocomplete => 1
+$AutocompleteMultiple => 0
</%ARGS>
diff --git a/rt/share/html/Elements/Error b/rt/share/html/Elements/Error
index 5ca1838..72bea69 100755
--- a/rt/share/html/Elements/Error
+++ b/rt/share/html/Elements/Error
@@ -48,7 +48,7 @@
% $m->callback( %ARGS, error => $error );
% unless ($SuppressHeader) {
-<& /Elements/Header, Code => $Code, Why => $Why, Title => $Title &>
+<& /Elements/Header, Title => $Title &>
<& /Elements/Tabs &>
% }
@@ -60,14 +60,11 @@
<%$Details%>
</div>
-<%cleanup>
-$m->comp('/Elements/Footer');
-$m->abort();
-</%cleanup>
+<& /Elements/Footer &>
+% $m->abort;
<%args>
$Actions => []
-$Code => undef
$Details => ''
$Title => loc("RT Error")
$Why => loc("the calling component did not specify why"),
@@ -75,13 +72,13 @@ $SuppressHeader => 0,
</%args>
<%INIT>
-my $error = "WebRT: $Why";
+my $error = $Why;
$error .= " ($Details)" if defined $Details && length $Details;
$RT::Logger->error( $error );
if ( $session{'REST'} ) {
- $r->content_type('text/plain');
+ $r->content_type('text/plain; charset=utf-8');
$m->out( "Error: " . $Why . "\n" );
$m->out( $Details . "\n" ) if defined $Details && length $Details;
$m->abort();
diff --git a/rt/share/html/Elements/BevelBoxRaisedEnd b/rt/share/html/Elements/FindUser
index e51efe2..6a28582 100755..100644
--- a/rt/share/html/Elements/BevelBoxRaisedEnd
+++ b/rt/share/html/Elements/FindUser
@@ -45,6 +45,6 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
- </td>
-</tr>
-</table>
+<&|/Widgets/TitleBox, title => loc('Find a user')&>
+<& /Elements/GotoUser &>
+</&>
diff --git a/rt/share/html/Elements/BevelBoxRaisedStart b/rt/share/html/Elements/FoldStanzaJS
index 9c9b410..b2cf0a5 100755..100644
--- a/rt/share/html/Elements/BevelBoxRaisedStart
+++ b/rt/share/html/Elements/FoldStanzaJS
@@ -45,6 +45,6 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<table cellspacing="0" cellpadding="0" width="100%" height="100%">
- <tr>
- <td width="100%" height="100%">
+<div
+ class="message-stanza-folder closed"
+ onclick="fold_message_stanza(this, <%loc('Show quoted text') |n,j%>, <%loc('Hide quoted text') |n,j%>);"><%loc('Show quoted text')%></div>
diff --git a/rt/share/html/Elements/SelectLinkType b/rt/share/html/Elements/GotoUser
index 5d70f04..214d232 100755..100644
--- a/rt/share/html/Elements/SelectLinkType
+++ b/rt/share/html/Elements/GotoUser
@@ -45,13 +45,18 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<select name="<%$Name%>">
-<option <% ($Default eq 'MemberOf') ? 'selected="selected"' : '' %> value="MemberOf"><&|/l&>Member of</&></option>
-<option <% ($Default eq 'DependsOn') ? 'selected="selected"' : '' %> value="DependsOn"><&|/l&>Depends on</&></option>
-<option <% ($Default eq 'RefersTo') ? 'selected="selected"' : '' %> value="RefersTo"><&|/l&>Refers to</&></option>
-</select>
-
+<form name="UserSearch" method="post" action="<% RT->Config->Get('WebPath') %>/User/Search.html">
+<input type="text" name="UserString" value="<% $Default %>" data-autocomplete="Users" data-autocomplete-return="Name" id="autocomplete-UserString" />
+<script type="text/javascript">
+jQuery(function(){
+ // Jump directly to the page if a user is chosen
+ jQuery("#autocomplete-UserString").on("autocompleteselect", function( event, ui ) {
+ document.location = RT.Config.WebPath + "/User/Summary.html?id=" + ui.item.id;
+ });
+});
+</script>
+<input type="submit" name="UserSearch" value="<&|/l&>Search</&>" class="button" />
+</form>
<%ARGS>
-$Name => "LinkType"
$Default => ''
</%ARGS>
diff --git a/rt/share/html/Elements/Header b/rt/share/html/Elements/Header
index 664da06..1e7eb35 100755
--- a/rt/share/html/Elements/Header
+++ b/rt/share/html/Elements/Header
@@ -56,8 +56,6 @@
&>
<%INIT>
#for "Site CSS from theme editor" below
-#use Scalar::Util qw(blessed);
-
$r->headers_out->{'Pragma'} = 'no-cache';
$r->headers_out->{'Cache-control'} = 'no-cache';
@@ -75,17 +73,23 @@ my $style = $session{'CurrentUser'}
my @css_files;
if ( RT->Config->Get('DevelMode') ) {
- @css_files = ( "$style/main.css", RT->Config->Get('CSSFiles' ) );
+ @css_files = map { "/static/css/$_" } "$style/main.css", RT->Config->Get('CSSFiles');
}
else {
my $key =
RT::Interface::Web::SquishedCSS( $style )->Key;
- @css_files = "$style-squished-$key.css";
+ @css_files = "/NoAuth/css/$style/squished-$key.css";
}
-my $head = '';
+# We use BodyClass in its $ARGS form so that callbacks have a chance to
+# massage it
+push @{$ARGS{'BodyClass'}}, lc $style;
-#XXX $head .= <& /Elements/Framekiller &>;
+if (RT->Config->Get("UseSideBySideLayout", $session{'CurrentUser'})) {
+ push @{$ARGS{'BodyClass'}}, 'sidebyside';
+}
+
+my $head = '';
if ($Refresh && $Refresh =~ /^(\d+)/ && $1 > 0) {
my $URL = $m->notes->{RefreshURL}; $URL = $URL ? ";URL=$URL" : "";
@@ -96,15 +100,17 @@ my $WebPath = RT->Config->Get('WebPath');
my $WebImagesURL = RT->Config->Get('WebImagesURL');
my $squished = RT->Config->Get('DevelMode') ? '' : '-squished';
-$head .= qq(<link rel="shortcut icon" href="${WebImagesURL}favicon.png" type="image/png" />\n);
-for my $cssfile ( @css_files ) {
- $head .= qq(<link rel="stylesheet" href="$WebPath/NoAuth/css/$cssfile" type="text/css" media="all" />\n);
+if ( $JavaScript ) {
+ $head .= $m->scomp('JavascriptConfig');
}
-$head .= qq(<link rel="stylesheet" href="$WebPath/NoAuth/css/print.css" type="text/css" media="print" />\n);
+
+for my $cssfile ( @css_files ) {
+ $head .= qq(<link rel="stylesheet" href="$WebPath$cssfile" type="text/css" media="all" />\n);
for (keys %{$LinkRel || {}}) {
$head .= qq(<link rel="$_" href="$WebPath) . $LinkRel->{$_} . '" />';
}
+$head .= qq(<link rel="shortcut icon" href="${WebImagesURL}favicon.png" type="image/png" />\n);
if ( $RSSAutoDiscovery ) {
$head .= qq(<link rel="alternate" href="$RSSAutoDiscovery" type="application/rss+xml" title="RSS RT Search" />);
@@ -112,29 +118,23 @@ if ( $RSSAutoDiscovery ) {
if ($JavaScript) {
$head .= $m->scomp('HeaderJavascript', focus => $Focus, onload => $onload, RichText => $RichText );
-}
-if ($JavaScript) {
my $stylesheet_plugin = "/NoAuth/css/$style/InHeader";
if ($m->comp_exists($stylesheet_plugin) ) {
$head .= $m->scomp($stylesheet_plugin);
}
}
-#<!-- Site CSS from theme editor -->
-#<style type="text/css" media="all" id="sitecss">
-#%# Header is used when there isn't a database (such as in the Installer) which means there is no
-#%# RT::System object, nor are there attributes.
-#% if (blessed(RT->System) and my $attr = RT->System->FirstAttribute('UserCSS')) {
-#<% $attr->Content |n %>
-#% }
-#</style>
-
# $m->callback( %ARGS, CallbackName => 'Head' );
$head .= $m->scomp( '/Elements/Callback', _CallbackName => 'Head', %ARGS );
-my $sbs = RT->Config->Get("UseSideBySideLayout", $session{'CurrentUser'}) ? ' sidebyside' : '';
-my $etc = qq[ class="\L$style$sbs" ];
+if ($JavaScript) {
+ $head .= $m->scomp('HeaderJavascript', focus => $Focus, onload => $onload, RichText => $RichText );
+}
+
+#XXX $head .= <& /Elements/Framekiller &>;
+
+my $etc = ' class="'. join( '',@{$ARGS{'BodyClass'}}). '" ';
$etc .= qq[ id="comp-$id"] if $id;
</%INIT>
@@ -143,15 +143,13 @@ $etc .= qq[ id="comp-$id"] if $id;
#$Focus => 'focus'
$Focus => ''
$Title => 'RT'
-$Code => undef
$Refresh => 0
-$Why => undef
$ShowBar => 1
$URL => undef
$RSSAutoDiscovery => undef
$onload => undef
$LinkRel => undef
-$JavaScript => 1
$SkipDoctype => 0
$RichText => 1
+$BodyClass => undef
</%ARGS>
diff --git a/rt/share/html/Elements/HeaderJavascript b/rt/share/html/Elements/HeaderJavascript
index 4bba559..79ee74e 100644
--- a/rt/share/html/Elements/HeaderJavascript
+++ b/rt/share/html/Elements/HeaderJavascript
@@ -51,37 +51,27 @@ $onload => undef
</%args>
% for my $jsfile ( @js_files ) {
-<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/js/<% $jsfile %>"></script>
+<script type="text/javascript" src="<%RT->Config->Get('WebPath')%><% $jsfile %>"></script>
% }
-% if ( $RichText and RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'}) ) {
-<script type="text/javascript" src="<%RT->Config->Get('WebPath')%>/NoAuth/RichText/ckeditor.js"></script>
-% }
<script type="text/javascript"><!--
- jQuery( loadTitleBoxStates );
+jQuery( loadTitleBoxStates );
% if ( $focus ) {
- jQuery(function () { focusElementById(<% $focus |n,j%>) });
+ jQuery(function () { jQuery(<% $focus |n,j%>).focus() });
% }
% if ( $onload ) {
jQuery( <% $onload |n %> );
% }
-
-% if ( $RichText and RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'})) {
- jQuery().ready(function () { ReplaceAllTextareas() });
-% }
--></script>
-<%ARGS>
-$RichText => 1
-</%ARGS>
<%INIT>
my @js_files;
if ( RT->Config->Get('DevelMode') ) {
- @js_files = RT->Config->Get('JSFiles' );
+ @js_files = map { $_ =~ m{^/} ? $_ : "/static/js/$_" } RT::Interface::Web->JSFiles();
}
else {
my $key = RT::Interface::Web::SquishedJS()->Key;
- @js_files = "squished-$key.js";
+ @js_files = "/NoAuth/js/squished-$key.js";
}
</%INIT>
diff --git a/rt/share/html/Elements/ShowUserConcise b/rt/share/html/Elements/JavascriptConfig
index 1fa970e..9437567 100644
--- a/rt/share/html/Elements/ShowUserConcise
+++ b/rt/share/html/Elements/JavascriptConfig
@@ -45,23 +45,40 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-%# Released under the terms of version 2 of the GNU Public License
-<% $display |n %>\
-<%ARGS>
-$User => undef
-$Address => undef
-</%ARGS>
-<%INIT>
-if ( !$User && $Address ) {
- $User = RT::User->new( $session{'CurrentUser'} );
- $User->LoadByEmail( $Address->address );
- if ( $User->Id ) {
- $Address = '';
- } else {
- $Address = $Address->address;
- }
+<%init>
+my $Config = {};
+$Config->{$_} = RT->Config->Get( $_, $session{CurrentUser} )
+ for qw(rtname WebPath MessageBoxRichTextHeight);
+
+my $CurrentUser = {};
+if ($session{CurrentUser} and $session{CurrentUser}->id) {
+ $CurrentUser->{$_} = $session{CurrentUser}->$_
+ for qw(id Name EmailAddress RealName);
+
+ $CurrentUser->{Privileged} = $session{CurrentUser}->Privileged
+ ? JSON::true : JSON::false;
+
+ $Config->{WebHomePath} = RT->Config->Get("WebPath")
+ . (!$session{CurrentUser}->Privileged ? "/SelfService" : "");
}
-my $display = $Address || $User->RealName || $User->Name;
- $display = $m->interp->apply_escapes( $display, 'h' )
- unless $ARGS{'NoEscape'};
-</%INIT>
+
+my $Catalog = {
+ quote_in_filename => "Filenames with double quotes can not be uploaded.", #loc
+};
+$_ = loc($_) for values %$Catalog;
+
+$m->callback(
+ CallbackName => "Data",
+ CurrentUser => $CurrentUser,
+ Config => $Config,
+ Catalog => $Catalog,
+);
+</%init>
+<script>
+window.RT = {};
+RT.CurrentUser = <% JSON( $CurrentUser ) |n%>;
+RT.Config = <% JSON( $Config ) |n%>;
+
+RT.I18N = {};
+RT.I18N.Catalog = <% JSON( $Catalog ) |n %>;
+</script>
diff --git a/rt/share/html/Elements/ListActions b/rt/share/html/Elements/ListActions
index 2b74c38..29bcf6b 100755
--- a/rt/share/html/Elements/ListActions
+++ b/rt/share/html/Elements/ListActions
@@ -45,15 +45,18 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+% $m->out($$_) for grep {ref $_} @actions;
+% if (grep {not ref $_} @actions) {
<div class="results">
<&| /Widgets/TitleBox, title => loc('Results'), %{$titlebox || {}} &>
<ul class="action-results">
-% foreach my $action (@actions) {
+% foreach my $action (grep {not ref $_} @actions) {
<li><%$action%></li>
% }
</ul>
</&>
</div>
+% }
<%init>
# backward compatibility, don't use array in new code, but use keyed hash
diff --git a/rt/share/html/Elements/ListMenu b/rt/share/html/Elements/ListMenu
index 45949c6..5c2a3d5 100644
--- a/rt/share/html/Elements/ListMenu
+++ b/rt/share/html/Elements/ListMenu
@@ -47,6 +47,7 @@
%# END BPS TAGGED BLOCK }}}
<%args>
$menu
+$show_children => undef
</%args>
<ul class="list-menu">
% for my $child ($menu->children) {
@@ -55,6 +56,9 @@ $menu
<span class="description"><% $description %></span>\
% }
</li>
+% if ($show_children && $child->children) {
+<& /Elements/ListMenu, menu => $child &>
+% }
% }
</ul>
diff --git a/rt/share/html/Elements/Login b/rt/share/html/Elements/Login
index 2c48294..cd38b29 100755
--- a/rt/share/html/Elements/Login
+++ b/rt/share/html/Elements/Login
@@ -46,7 +46,7 @@
%#
%# END BPS TAGGED BLOCK }}}
% $m->callback( %ARGS, CallbackName => 'Header' );
-<& /Elements/Header, Title => loc('Login'), Focus => 'user', RichText => 0 &>
+<& /Elements/Header, Title => loc('Login'), Focus => '#user', RichText => 0 &>
<div id="body" class="login-body">
@@ -63,7 +63,7 @@
<& LoginRedirectWarning, %ARGS &>
-% unless (RT->Config->Get('WebExternalAuth') and !RT->Config->Get('WebFallbackToInternalAuth')) {
+% unless (RT->Config->Get('WebRemoteUserAuth') and !RT->Config->Get('WebFallbackToRTLogin')) {
<form id="login" name="login" method="post" action="<% RT->Config->Get('WebPath') %>/NoAuth/Login.html">
<div class="input-row">
@@ -73,7 +73,7 @@
<div class="input-row">
<span class="label"><&|/l&>Password</&>:</span>
- <span class="input"><input type="password" name="pass" autocomplete="off" /></span>
+ <span class="input"><input type="password" name="pass" <% RT->Config->Get('AllowLoginPasswordAutoComplete') ? '' : 'autocomplete="off"' | n %> /></span>
</div>
<input type="hidden" name="next" value="<% $next %>" />
@@ -97,6 +97,7 @@ jQuery(function(){
</form>
% }
</&>
+<& /Elements/LoginHelp &>
</div><!-- #login-box -->
% $m->callback( %ARGS, CallbackName => 'AfterForm' );
</div><!-- #login-body -->
diff --git a/rt/share/html/Elements/MyTickets b/rt/share/html/Elements/LoginHelp
index 583d17d..fbb4c0c 100755..100644
--- a/rt/share/html/Elements/MyTickets
+++ b/rt/share/html/Elements/LoginHelp
@@ -45,5 +45,10 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-%# DEPRECATED
-<& /Elements/ShowSearch, Name => 'My Tickets' &>
+<div class="login-help">
+<&|/l, RT->Config->Get('OwnerEmail')&>For local help, please contact [_1]</&>
+</div>
+<%init>
+my $source = RT->Config->Meta('OwnerEmail')->{Source};
+return unless $source->{SiteConfig} or $source->{Extension};
+</%init>
diff --git a/rt/share/html/Elements/Logo b/rt/share/html/Elements/Logo
index 23c73e8..80e8697 100644
--- a/rt/share/html/Elements/Logo
+++ b/rt/share/html/Elements/Logo
@@ -53,9 +53,7 @@
% } else {
<a href="<%$ARGS{'LogoLinkURL'}||RT->Config->Get('LogoLinkURL')%>"><img
src="<%$ARGS{'LogoURL'}||RT->Config->Get('LogoURL')%>"
- alt="<%loc($ARGS{'LogoAltText'}||RT->Config->Get('LogoAltText'))%>"
- width="<%$ARGS{'LogoImageWidth'}||RT->Config->Get('LogoImageWidth')%>"
- height="<%$ARGS{'LogoImageHeight'}||RT->Config->Get('LogoImageHeight')%>" /></a>
+ alt="<%loc($ARGS{'LogoAltText'}||RT->Config->Get('LogoAltText'))%>" /></a>
% }
% if ( $ShowName ) {
<span class="rtname"><% $Name || loc("RT for [_1]", RT->Config->Get('rtname')) %></span>
@@ -67,7 +65,6 @@ if ( exists $ARGS{'show_name'} ) {
$ShowName = delete $ARGS{'show_name'};
}
-use Scalar::Util qw(blessed);
my $user_logo = blessed $RT::System ? $RT::System->FirstAttribute('UserLogo') : undef;
# If we have the attribute, but no content, we don't really have a user logo
diff --git a/rt/share/html/Elements/MakeClicky b/rt/share/html/Elements/MakeClicky
index 4607ba0..4d28771 100644
--- a/rt/share/html/Elements/MakeClicky
+++ b/rt/share/html/Elements/MakeClicky
@@ -50,7 +50,7 @@ use Regexp::Common qw(URI);
my $escaper = sub {
my $content = shift;
- RT::Interface::Web::EscapeUTF8( \$content );
+ RT::Interface::Web::EscapeHTML( \$content );
return $content;
};
@@ -61,15 +61,19 @@ my %actions = (
},
url => sub {
my %args = @_;
+ my $post = "";
+ $post = ")" if $args{value} !~ /\(/ and $args{value} =~ s/\)$//;
$args{value} = $escaper->($args{value});
- my $result = qq{[<a target="new" href="$args{value}">}. loc('Open URL') .qq{</a>]};
- return $args{value} . qq{ <span class="clickylink">$result</span>};
+ my $result = qq{[<a target="_blank" href="$args{value}">}. loc('Open URL') .qq{</a>]};
+ return $args{value} . qq{ <span class="clickylink">$result</span>$post};
},
url_overwrite => sub {
my %args = @_;
+ my $post = "";
+ $post = ")" if $args{value} !~ /\(/ and $args{value} =~ s/\)$//;
$args{value} = $escaper->($args{value});
- my $result = qq{<a target="new" href="$args{value}">$args{value}</a>};
- return qq{<span class="clickylink">$result</span>};
+ my $result = qq{<a target="_blank" href="$args{value}">$args{value}</a>};
+ return qq{<span class="clickylink">$result</span>$post};
},
);
@@ -144,14 +148,14 @@ if ( defined $cache ) {
}
unless ( $regexp ) {
- RT::Interface::Web::EscapeUTF8( $content ) unless $html;
+ RT::Interface::Web::EscapeHTML( $content ) unless $html;
return;
}
my $pos = 0;
while ( $$content =~ /($regexp)/gsio ) {
my $match = $1;
- next if $` =~ /href=(?:&quot;|")$/;
+ next if $` =~ /\w+=(?:&quot;|")$/;
my $skipped_len = pos($$content) - $pos - length($match);
if ( $skipped_len > 0 ) {
my $plain;
diff --git a/rt/share/html/Elements/Menu b/rt/share/html/Elements/Menu
index 16535c8..fb109a9 100755
--- a/rt/share/html/Elements/Menu
+++ b/rt/share/html/Elements/Menu
@@ -45,36 +45,6 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% return unless ($menu);
-<%" " x $depth%><ul<%$id ? ' id="'.$id.'"' : '' |n%><% $toplevel? ' class="toplevel"' : '' |n %>>
-% for my $child ($menu->children) {
-% my $item_id = lc(($parent_id? $parent_id."-" : "") .$child->key);
-% $item_id =~ s/\s/-/g;
-% my @classes;
-% push @classes, 'has-children' if $child->has_children;
-% push @classes, 'active' if $child->active;
-<%" " x ($depth+1)%><li id="li-<%$item_id%>"\
-% if (@classes) {
- class="<% join ' ', @classes %>"\
-% }
->\
-% if ($child->raw_html) {
-<% $child->raw_html |n %>
-% } else {
-% my $url = $m->interp->apply_escapes((not $child->path or $child->path =~ m{^\w+:/}) ? $child->path : RT->Config->Get('WebPath').$child->path, 'h');
-<a id="<%$item_id%>" class="menu-item <% $child->class || '' %>"<% $child->path ? ' href="'.$url.'"' : '' |n%><% $child->target ? ' target="'.$m->interp->apply_escapes($child->target, 'h').'"' : '' |n %>>\
-<% $child->escape_title ? $m->interp->apply_escapes($child->title, 'h') : $child->title |n %>\
-</a>\
-% }
-% if ($child->has_children) {
-
-<& Menu, menu => $child, toplevel => 0, parent_id => ($parent_id? $parent_id."-": '').$child->key, depth=> ($depth+1) &>
-<%" " x ($depth+1)%></li>
-% } else {
-</li>
-% }
-% }
-<%" " x $depth%></ul>\
<%ARGS>
$menu
$id => undef
@@ -82,6 +52,6 @@ $toplevel => 1
$parent_id => ''
$depth => 0
</%ARGS>
-<%init>
-$id = $m->interp->apply_escapes($id, 'h');
-</%init>
+<%INIT>
+RenderMenu( %ARGS );
+</%INIT>
diff --git a/rt/share/html/Elements/MessageBox b/rt/share/html/Elements/MessageBox
index c4a2de9..bcc64d4 100755
--- a/rt/share/html/Elements/MessageBox
+++ b/rt/share/html/Elements/MessageBox
@@ -45,13 +45,13 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<textarea autocomplete="off" class="messagebox" <% $width_attr %>="<% $Width %>" rows="<% $Height %>" <% $wrap_type |n %> name="<% $Name %>" id="<% $Name %>">\
+<textarea autocomplete="off" class="messagebox <% $Type eq 'text/html' ? 'richtext' : '' %>" <% $width_attr %>="<% $Width %>" rows="<% $Height %>" <% $wrap_type |n %> name="<% $Name %>" id="<% $Name %>">\
% $m->comp('/Articles/Elements/IncludeArticle', %ARGS) if $IncludeArticle;
% $m->callback( %ARGS, SignatureRef => \$signature );
<% $Default || '' %><% $message %><% $signature %></textarea>
% $m->callback( %ARGS, CallbackName => 'AfterTextArea' );
-% if (RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'})) {
-<input type="text" style="display:none" name="<% $Name %>Type" id="<% $Name %>Type" value="<% $m->request_args->{$Name."Type"}||'' %>" />
+% if ($Type eq 'text/html') {
+<input type="text" style="display:none" name="<% $Name %>Type" id="<% $Name %>Type" value="<% $m->request_args->{$Name."Type"}||$Type %>" />
% }
<%INIT>
@@ -60,19 +60,25 @@ my $message = '';
if ( $QuoteTransaction ) {
my $transaction = RT::Transaction->new( $session{'CurrentUser'} );
$transaction->Load( $QuoteTransaction );
- $message = $transaction->Content( Quote => 1 );
+ $message = $transaction->Content( Quote => 1, Type => $Type );
}
my $signature = '';
if ( $IncludeSignature and my $text = $session{'CurrentUser'}->UserObj->Signature ) {
- $signature = "-- \n". $text;
+ $signature = "-- \n". $text;
+ if ($Type eq 'text/html') {
+ $signature =~ s/&/&amp;/g;
+ $signature =~ s/</&lt;/g;
+ $signature =~ s/>/&gt;/g;
+ $signature =~ s/"/&quot;/g; # "//;
+ $signature =~ s/'/&#39;/g; # '//;
+ $signature =~ s{\n}{<br />}g;
+ $signature = "<p>$signature</p>";
+ }
}
# wrap="something" seems to really break IE + richtext
-my $wrap_type = '';
-if ( not RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'}) ) {
- $wrap_type = 'wrap="' . $m->interp->apply_escapes($Wrap, 'h') . '"';
-}
+my $wrap_type = $Type eq 'text/html' ? '' : 'wrap="soft"';
# If there's no cols specified, we want to set the width to 100% in CSS
my $width_attr;
@@ -90,7 +96,7 @@ $Name => 'Content'
$Default => ''
$Width => RT->Config->Get('MessageBoxWidth', $session{'CurrentUser'} )
$Height => RT->Config->Get('MessageBoxHeight', $session{'CurrentUser'} ) || 15
-$Wrap => RT->Config->Get('MessageBoxWrap', $session{'CurrentUser'} ) || 'SOFT'
$IncludeSignature => RT->Config->Get('MessageBoxIncludeSignature');
$IncludeArticle => 1;
+$Type => RT->Config->Get('MessageBoxRichText', $session{'CurrentUser'}) ? 'text/html' : 'text/plain';
</%ARGS>
diff --git a/rt/share/html/Elements/MyRT b/rt/share/html/Elements/MyRT
index e8b0846..0d07eaa 100644
--- a/rt/share/html/Elements/MyRT
+++ b/rt/share/html/Elements/MyRT
@@ -53,9 +53,9 @@
% $show_cb->($_) foreach @$body;
</td>
-% if ( $summary ) {
+% if ( $sidebar ) {
<td class="boxcontainer">
-% $show_cb->($_) foreach @$summary;
+% $show_cb->($_) foreach @$sidebar;
</td>
% }
@@ -63,24 +63,24 @@
% $m->callback( ARGSRef => \%ARGS, CallbackName => 'AfterTable' );
<%INIT>
-# XXX: we don't use this, but should.
my %allowed_components = map {$_ => 1} @{RT->Config->Get('HomepageComponents')};
my $user = $session{'CurrentUser'}->UserObj;
-$Portlets ||= $user->Preferences('HomepageSettings');
unless ( $Portlets ) {
my ($defaults) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
- $Portlets = $defaults ? $defaults->Content : {};
+ $Portlets = $user->Preferences(
+ HomepageSettings => $defaults ? $defaults->Content : {}
+ );
}
$m->callback( CallbackName => 'MassagePortlets', Portlets => $Portlets );
-my ($body, $summary) = @{$Portlets}{qw(body summary)};
+my ($body, $sidebar) = @{$Portlets}{qw(body sidebar)};
unless( $body && @$body ) {
- $body = $summary || [];
- $summary = undef;
+ $body = $sidebar || [];
+ $sidebar = undef;
}
-$summary = undef unless $summary && @$summary;
+$sidebar = undef unless $sidebar && @$sidebar;
my $Rows = $user->Preferences( 'SummaryRows', ( RT->Config->Get('DefaultSummaryRows') || 10 ) );
@@ -89,12 +89,16 @@ my $show_cb = sub {
my $type = $entry->{type};
my $name = $entry->{'name'};
if ( $type eq 'component' ) {
- # XXX: security check etc.
- $m->comp( $name, %{ $entry->{arguments} || {} } );
+ if (!$allowed_components{$name}) {
+ $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $name), "h" ) );
+ }
+ else {
+ $m->comp( $name, %{ $entry->{arguments} || {} } );
+ }
} elsif ( $type eq 'system' ) {
$m->comp( '/Elements/ShowSearch', Name => $name, Override => { Rows => $Rows } );
} elsif ( $type eq 'saved' ) {
- $m->comp( '/Elements/ShowSearch', SavedSearch => $name, Override => { Rows => $Rows }, IgnoreMissing => 1 );
+ $m->comp( '/Elements/ShowSearch', SavedSearch => $name, Override => { Rows => $Rows } );
} else {
$RT::Logger->error("unknown portlet type '$type'");
}
diff --git a/rt/share/html/Elements/MyReminders b/rt/share/html/Elements/MyReminders
index 7619808..f4fbf5d 100755
--- a/rt/share/html/Elements/MyReminders
+++ b/rt/share/html/Elements/MyReminders
@@ -45,7 +45,6 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-%# DEPRECATED
<&|/Widgets/TitleBox,
class => 'reminders',
title => loc("My reminders"),
diff --git a/rt/share/html/Elements/MyRequests b/rt/share/html/Elements/MyRequests
deleted file mode 100755
index 357476c..0000000
--- a/rt/share/html/Elements/MyRequests
+++ /dev/null
@@ -1,49 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
-%# <sales@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-%# DEPRECATED
-<& /Elements/ShowSearch, Name => 'My Requests' &>
diff --git a/rt/share/html/Elements/PageLayout b/rt/share/html/Elements/PageLayout
index 9e8aecb..edd71d1 100755
--- a/rt/share/html/Elements/PageLayout
+++ b/rt/share/html/Elements/PageLayout
@@ -55,6 +55,11 @@
% if (0) { ## new ticket via customer, and we already have a ticket search box
<div id="topactions"><& /Elements/WidgetBar, menu => PageWidgets() &></div>
% }
+
+% if ($m->comp_exists($stylesheet_plugin) ) {
+<& $stylesheet_plugin &>
+% }
+
<div id="body">
% $m->callback( %ARGS, CallbackName => 'BeforeBody' );
% $m->flush_buffer(); # we've got the page laid out, let's flush the buffer;
@@ -62,3 +67,9 @@
$title => $m->callers(-1)->path
$show_menu => 1
</%ARGS>
+<%init>
+my $style = $session{'CurrentUser'}
+ ? $session{'CurrentUser'}->Stylesheet
+ : RT->Config->Get('WebDefaultStylesheet');
+my $stylesheet_plugin = "/NoAuth/css/".$style."/AfterMenus";
+</%init>
diff --git a/rt/share/html/Elements/QueryString b/rt/share/html/Elements/QueryString
index 8bff988..447cc5d 100644
--- a/rt/share/html/Elements/QueryString
+++ b/rt/share/html/Elements/QueryString
@@ -54,11 +54,12 @@ for my $key (sort keys %ARGS) {
if( UNIVERSAL::isa( $value, 'ARRAY' ) ) {
push @params,
map $key ."=". $m->interp->apply_escapes( $_, 'u' ),
+ map defined $_? $_ : '',
@$value;
} else {
push @params, $key ."=". $m->interp->apply_escapes($value, 'u');
}
}
-return join '&', sort(@params);
+return join '&', @params;
</%INIT>
diff --git a/rt/share/html/Elements/QueueSummaryByLifecycle b/rt/share/html/Elements/QueueSummaryByLifecycle
index da31ebb..f21cb20 100644
--- a/rt/share/html/Elements/QueueSummaryByLifecycle
+++ b/rt/share/html/Elements/QueueSummaryByLifecycle
@@ -94,11 +94,14 @@ my $build_search_link = sub {
my $link_all = sub {
my ($queue, $all_statuses) = @_;
- return $build_search_link->($queue->{Name}, "(".join(" OR ", map "Status = '$_'", @$all_statuses).")");
+ my @escaped = @{$all_statuses};
+ s{(['\\])}{\\$1}g for @escaped;
+ return $build_search_link->($queue->{Name}, "(".join(" OR ", map "Status = '$_'", @escaped).")");
};
my $link_status = sub {
my ($queue, $status) = @_;
+ $status =~ s{(['\\])}{\\$1}g;
return $build_search_link->($queue->{Name}, "Status = '$status'");
};
@@ -120,14 +123,14 @@ $m->callback( CallbackName => 'Filter', Queues => \@queues );
{ id => $_->Id,
Name => $_->Name,
Description => $_->Description || '',
- Lifecycle => $_->Lifecycle->Name,
+ Lifecycle => $_->Lifecycle,
}
} grep $_, @queues;
my %lifecycle;
for my $queue (@queues) {
- my $cycle = RT::Lifecycle->Load( $queue->{'Lifecycle'} );
+ my $cycle = RT::Lifecycle->Load( Name => $queue->{'Lifecycle'} );
$lifecycle{ lc $cycle->Name } = $cycle;
}
@@ -145,9 +148,11 @@ my $statuses = {};
use RT::Report::Tickets;
my $report = RT::Report::Tickets->new( RT->SystemUser );
+my @escaped = @statuses;
+s{(['\\])}{\\$1}g for @escaped;
my $query =
"(".
- join(" OR ", map {s{(['\\])}{\\$1}g; "Status = '$_'"} @statuses) #'
+ join(" OR ", map {"Status = '$_'"} @escaped) #'
.") AND (".
join(' OR ', map "Queue = ".$_->{id}, @queues)
.")";
diff --git a/rt/share/html/Elements/QueueSummaryByStatus b/rt/share/html/Elements/QueueSummaryByStatus
index 3acf9c9..704bca6 100644
--- a/rt/share/html/Elements/QueueSummaryByStatus
+++ b/rt/share/html/Elements/QueueSummaryByStatus
@@ -119,14 +119,14 @@ $m->callback( CallbackName => 'Filter', Queues => \@queues );
{ id => $_->Id,
Name => $_->Name,
Description => $_->Description || '',
- Lifecycle => $_->Lifecycle->Name,
+ Lifecycle => $_->Lifecycle,
}
} grep $_, @queues;
my %lifecycle;
for my $queue (@queues) {
- my $cycle = RT::Lifecycle->Load( $queue->{'Lifecycle'} );
+ my $cycle = RT::Lifecycle->Load( Name => $queue->{'Lifecycle'} );
$lifecycle{ lc $cycle->Name } = $cycle;
}
diff --git a/rt/share/html/Elements/QuickCreate b/rt/share/html/Elements/QuickCreate
index fa03f20..b016314 100644
--- a/rt/share/html/Elements/QuickCreate
+++ b/rt/share/html/Elements/QuickCreate
@@ -69,7 +69,7 @@
</tr>
<tr class="input-row">
<td class="label"><&|/l&>Requestors</&>:</td>
- <td colspan="3" class="value"><& /Elements/EmailInput, Name => 'Requestors', Size => '40', Default => $args->{Requestors} || $session{CurrentUser}->EmailAddress &></td>
+ <td colspan="3" class="value"><& /Elements/EmailInput, Name => 'Requestors', Size => '40', Default => $args->{Requestors} || $session{CurrentUser}->EmailAddress, AutocompleteMultiple => 1 &></td>
</tr>
<tr class="input-row">
<td class="labeltop"><&|/l&>Content</&>:</td>
diff --git a/rt/share/html/Elements/RT__Article/ColumnMap b/rt/share/html/Elements/RT__Article/ColumnMap
index 4abb068..5c90427 100644
--- a/rt/share/html/Elements/RT__Article/ColumnMap
+++ b/rt/share/html/Elements/RT__Article/ColumnMap
@@ -48,6 +48,7 @@
<%ARGS>
$Name => undef
$Attr => undef
+$GenericMap => {}
</%ARGS>
@@ -75,16 +76,6 @@ $COLUMN_MAP = {
title => 'Class', # loc
value => sub { $_[0]->ClassObj->Name },
},
- CreatedRelative => {
- attribute => 'Created',
- title => 'Created', # loc
- value => sub { $_[0]->CreatedObj->AgeAsString },
- },
- LastUpdatedRelative => {
- attribute => 'LastUpdated',
- title => 'LastUpdated', # loc
- value => sub { $_[0]->LastUpdatedObj->AgeAsString },
- },
Topics => {
title => 'Topics', # loc
value => sub {
@@ -102,6 +93,6 @@ $COLUMN_MAP = {
</%ONCE>
<%init>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%init>
diff --git a/rt/share/html/Elements/RT__Class/ColumnMap b/rt/share/html/Elements/RT__Class/ColumnMap
index 0916065..8bc8e4a 100644
--- a/rt/share/html/Elements/RT__Class/ColumnMap
+++ b/rt/share/html/Elements/RT__Class/ColumnMap
@@ -48,14 +48,10 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- value => sub { return $_[0]->id },
- },
Name => {
title => 'Name', # loc
attribute => 'Name',
@@ -66,11 +62,16 @@ my $COLUMN_MAP = {
attribute => 'Description',
value => sub { return $_[0]->Description() },
},
+ Disabled => {
+ title => 'Status', # loc
+ attribute => 'Disabled',
+ value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') },
+ },
};
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__CustomField/ColumnMap b/rt/share/html/Elements/RT__CustomField/ColumnMap
index 765be29..764eaca 100644
--- a/rt/share/html/Elements/RT__CustomField/ColumnMap
+++ b/rt/share/html/Elements/RT__CustomField/ColumnMap
@@ -48,17 +48,12 @@
<%ARGS>
$Name => undef
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->id },
- },
Disabled => {
- title => \'&nbsp;',
+ title => 'Status', # loc
attribute => 'Disabled',
value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') },
},
@@ -66,7 +61,7 @@ my $COLUMN_MAP = {
map(
{ my $c = $_; $c => {
title => $c, attribute => $c,
- value => sub { return $_[0]->$c() },
+ value => sub { return $_[0]->$c() },
} }
qw(Name Description Type LookupType Pattern)
),
@@ -74,26 +69,26 @@ my $COLUMN_MAP = {
{ my $c = $_; my $short = $c; $short =~ s/^Friendly//;
$c => {
title => $short, attribute => $short,
- value => sub { return $_[0]->$c() },
+ value => sub { return $_[0]->$c() },
} }
qw(FriendlyLookupType FriendlyType FriendlyPattern)
),
MaxValues => {
title => 'MaxValues', # loc
- attribute => 'MaxValues',
- value => sub {
+ attribute => 'MaxValues',
+ value => sub {
my $v = $_[0]->MaxValues;
return !$v ? $_[0]->loc('unlimited') : $v == 0 ? $_[0]->loc('one') : $v;
},
},
- AppliedTo => {
- title => 'Applied', # loc
- value => sub {
- if ( $_[0]->IsApplied ) {
+ AddedTo => {
+ title => 'Added', # loc
+ value => sub {
+ if ( $_[0]->IsGlobal ) {
return $_[0]->loc('Global');
}
- my $collection = $_[0]->AppliedTo;
+ my $collection = $_[0]->AddedTo;
return '' unless $collection;
$collection->RowsPerPage(10);
@@ -121,13 +116,13 @@ my $COLUMN_MAP = {
my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
- onclick="setCheckbox(this.form, },
+ onclick="setCheckbox(this, },
$m->interp->apply_escapes($name,'j'),
- \qq{, this.checked)" />};
+ \qq{)" />};
},
value => sub {
my $id = $_[0]->id;
- return '' if $_[0]->IsApplied;
+ return '' if $_[0]->IsGlobal;
my $name = 'RemoveCustomField';
my $arg = $DECODED_ARGS->{ $name };
@@ -148,7 +143,7 @@ my $COLUMN_MAP = {
my $id = $_[0]->id;
my $context = $_[2] || 0;
- return '' unless $_[0]->IsApplied( $context );
+ return '' unless $_[0]->IsAdded( $context );
my $name = 'MoveCustomField';
my $args = $m->caller_args( 1 );
@@ -173,8 +168,10 @@ my $COLUMN_MAP = {
},
};
+$COLUMN_MAP->{'AppliedTo'} = $COLUMN_MAP->{'AddedTo'};
+
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__Dashboard/ColumnMap b/rt/share/html/Elements/RT__Dashboard/ColumnMap
index 3cc7c20..e9509a2 100644
--- a/rt/share/html/Elements/RT__Dashboard/ColumnMap
+++ b/rt/share/html/Elements/RT__Dashboard/ColumnMap
@@ -48,15 +48,10 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->Id },
- },
Name => {
title => 'Name', # loc
attribute => 'Name',
@@ -129,6 +124,6 @@ my $COLUMN_MAP = {
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__Group/ColumnMap b/rt/share/html/Elements/RT__Group/ColumnMap
index e336f77..a1d2558 100644
--- a/rt/share/html/Elements/RT__Group/ColumnMap
+++ b/rt/share/html/Elements/RT__Group/ColumnMap
@@ -48,15 +48,10 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->id },
- },
HasMember => {
title => 'Member', # loc
value => sub {
@@ -75,18 +70,22 @@ my $COLUMN_MAP = {
},
Name => {
title => 'Name', # loc
- attribute => 'Name',
- value => sub { return $_[0]->Name() },
+ attribute => 'Name',
+ value => sub { return $_[0]->Name() },
},
Description => {
title => 'Description', # loc
- attribute => 'Description',
- value => sub { return $_[0]->Description() },
+ attribute => 'Description',
+ value => sub { return $_[0]->Description() },
+ },
+ Disabled => {
+ title => 'Status', # loc
+ value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') },
},
};
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__Queue/ColumnMap b/rt/share/html/Elements/RT__Queue/ColumnMap
index 8bc3331..1878abe 100644
--- a/rt/share/html/Elements/RT__Queue/ColumnMap
+++ b/rt/share/html/Elements/RT__Queue/ColumnMap
@@ -48,17 +48,12 @@
<%ARGS>
$Name => undef
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->id },
- },
Disabled => {
- title => \'&nbsp;',
+ title => 'Status', # loc
attribute => 'Disabled',
value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') },
},
@@ -88,7 +83,15 @@ my $COLUMN_MAP = {
Lifecycle => {
title => 'Lifecycle',
attribute => 'Lifecycle',
- value => sub { return $_[0]->Lifecycle->Name },
+ value => sub { return $_[0]->Lifecycle },
+ },
+ ScripStage => {
+ title => 'Stage', # loc
+ value => sub {
+ my $os = RT::ObjectScrip->new( $_[0]->CurrentUser );
+ $os->LoadByCols( Scrip => $_[-1], ObjectId => $_[0]->id );
+ return $_[0]->loc( $os->FriendlyStage );
+ },
},
};
@@ -105,7 +108,7 @@ foreach my $field (qw(
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__SavedSearch/ColumnMap b/rt/share/html/Elements/RT__SavedSearch/ColumnMap
index dd02cbc..12a7081 100644
--- a/rt/share/html/Elements/RT__SavedSearch/ColumnMap
+++ b/rt/share/html/Elements/RT__SavedSearch/ColumnMap
@@ -48,15 +48,10 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->Id },
- },
Name => {
title => 'Name', # loc
attribute => 'Name',
@@ -80,6 +75,6 @@ my $COLUMN_MAP = {
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__Scrip/ColumnMap b/rt/share/html/Elements/RT__Scrip/ColumnMap
index eb2f065..e51d938 100644
--- a/rt/share/html/Elements/RT__Scrip/ColumnMap
+++ b/rt/share/html/Elements/RT__Scrip/ColumnMap
@@ -48,25 +48,38 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->id },
- },
Queue => {
title => 'Queue', # loc
value => sub {
- return $_[0]->QueueObj->Name if $_[0]->Queue;
- return $_[0]->loc('Global');
+ return $_[0]->loc('Global') if $_[0]->IsGlobal;
+ return join(", ", map {$_->Name} @{$_[0]->AddedTo->ItemsArrayRef});
},
},
QueueId => {
title => 'Queue', # loc
- value => sub { $_[0]->Queue },
+ value => sub {
+ return 0 if $_[0]->IsGlobal;
+ return join(", ", map {$_->Id} @{$_[0]->AddedTo->ItemsArrayRef});
+ },
+ },
+ From => {
+ title => 'Queue',
+ value => sub {
+ my $request_path = $HTML::Mason::Commands::r->path_info;
+ my $queue_id = $m->request_args->{'id'};
+ if ( $request_path =~ m{/Admin/Queues/Scrips\.html} and $queue_id ) {
+ return '&From=' . $queue_id;
+ } elsif ( $request_path =~ m{/Admin/Global/Scrips\.html} ) {
+ return '&From=Global';
+ }
+ else {
+ return q{};
+ }
+ },
},
Condition => {
title => 'Condition', # loc
@@ -78,30 +91,93 @@ my $COLUMN_MAP = {
},
Template => {
title => 'Template', # loc
- value => sub { return $_[0]->loc( $_[0]->TemplateObj->Name ) },
+ value => sub { return $_[0]->loc( $_[0]->Template ) },
},
AutoDescription => {
title => 'Condition, Action and Template', # loc
value => sub { return $_[0]->loc( "[_1] [_2] with template [_3]",
$_[0]->loc($_[0]->ConditionObj->Name),
$_[0]->loc($_[0]->ActionObj->Name),
- $_[0]->loc($_[0]->TemplateObj->Name),
+ $_[0]->loc($_[0]->Template),
) },
},
Description => {
title => 'Description', # loc
- attribute => 'Description',
- value => sub { return $_[0]->Description() },
+ attribute => 'Description',
+ value => sub { return $_[0]->Description() },
+ },
+ Disabled => {
+ title => 'Status', # loc
+ attribute => 'Disabled',
+ value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') },
+ },
+ RemoveCheckBox => {
+ title => sub {
+ my $name = 'RemoveScrip';
+ my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
+
+ return \qq{<input type="checkbox" name="${name}All" value="1" $checked
+ onclick="setCheckbox(this, '$name')" />};
+ },
+ value => sub {
+ my $id = $_[0]->id;
+ return '' if $_[0]->IsGlobal;
+
+ my $name = 'RemoveScrip';
+ my $arg = $m->request_args->{ $name };
+
+ my $checked = '';
+ if ( $arg && ref $arg ) {
+ $checked = 'checked="checked"' if grep $_ == $id, @$arg;
+ }
+ elsif ( $arg ) {
+ $checked = 'checked="checked"' if $arg == $id;
+ }
+ return \qq{<input type="checkbox" name="$name" value="$id" $checked />}
+ },
+ },
+ Move => {
+ title => 'Move', # loc
+ value => sub {
+ my $id = $_[0]->id;
+
+ my $context = $_[2] || 0;
+ return '' unless $_[0]->IsAdded( $context );
+
+ my $name = 'MoveScrip';
+ my $args = $m->caller_args( 1 );
+ my @pass = ref $args->{'PassArguments'}
+ ? @{$args->{'PassArguments'}}
+ : ($args->{'PassArguments'});
+ my %pass = map { $_ => $args->{$_} } grep exists $args->{$_}, @pass;
+
+ my $uri = RT->Config->Get('WebPath') . $m->request_path;
+
+ my @res = (
+ \'<a href="',
+ $uri .'?'. $m->comp("/Elements/QueryString", %pass, MoveScripUp => $id ),
+ \'">', loc('[Up]'), \'</a>',
+ \' <a href="',
+ $uri .'?'. $m->comp("/Elements/QueryString", %pass, MoveScripDown => $id ),
+ \'">', loc('[Down]'), \'</a>'
+ );
+
+ return @res;
+ },
},
Stage => {
- title => 'Stage', # loc
- attribute => 'Stage',
- value => sub { return $_[0]->Stage() },
+ title => 'Stage', # loc
+ value => sub {
+ my $os = RT::ObjectScrip->new( $_[0]->CurrentUser );
+ my $id = $_[0]->IsGlobal ? 0 : $_[-1];
+ $os->LoadByCols( Scrip => $_[0]->id, ObjectId => $id );
+ return $_[0]->loc( $os->FriendlyStage );
+ },
},
};
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__Template/ColumnMap b/rt/share/html/Elements/RT__Template/ColumnMap
index da712c2..142b695 100644
--- a/rt/share/html/Elements/RT__Template/ColumnMap
+++ b/rt/share/html/Elements/RT__Template/ColumnMap
@@ -48,24 +48,19 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->id },
- },
Name => {
title => 'Name', # loc
- attribute => 'Name',
- value => sub { return $_[0]->Name() },
+ attribute => 'Name',
+ value => sub { return $_[0]->Name() },
},
Description => {
title => 'Description', # loc
- attribute => 'Description',
- value => sub { return $_[0]->Description() },
+ attribute => 'Description',
+ value => sub { return $_[0]->Description() },
},
Queue => {
title => 'Queue', # loc
@@ -78,10 +73,28 @@ my $COLUMN_MAP = {
title => 'Queue', # loc
value => sub { $_[0]->Queue },
},
+ IsEmpty => {
+ title => 'Empty', # loc
+ value => sub { $_[0]->IsEmpty? $_[0]->loc('Yes') : $_[0]->loc('No') },
+ },
+ UsedBy => {
+ title => 'Used by scrips', # loc
+ value => sub {
+ my @res;
+ my $scrips = $_[0]->UsedBy;
+ while ( my $scrip = $scrips->Next ) {
+ push @res, ', ' if @res;
+ push @res, \'<a href="', RT->Config->Get('WebPath'), '/Admin/Scrips/Modify.html';
+ push @res, '?id='. $scrip->id;
+ push @res, \'" title="', $scrip->Description, \'">', $scrip->id, \'</a>';
+ }
+ return @res;
+ },
+ },
};
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/RT__Ticket/ColumnMap b/rt/share/html/Elements/RT__Ticket/ColumnMap
index 1668d32..cde8d59 100644
--- a/rt/share/html/Elements/RT__Ticket/ColumnMap
+++ b/rt/share/html/Elements/RT__Ticket/ColumnMap
@@ -48,6 +48,7 @@
<%ARGS>
$Name => undef
$Attr => undef
+$GenericMap => {}
</%ARGS>
@@ -57,20 +58,36 @@ my $COLUMN_MAP;
my $LinkCallback = sub {
my $method = shift;
- my $mode = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
- my $type = $RT::Ticket::LINKTYPEMAP{$method}{Type};
+ my $mode = $RT::Link::TYPEMAP{$method}{Mode};
+ my $type = $RT::Link::TYPEMAP{$method}{Type};
my $other_mode = ($mode eq "Target" ? "Base" : "Target");
my $mode_uri = $mode.'URI';
- my $local_type = 'Local'.$mode;
return sub {
+ my $ObjectType = $_[2]||'';
map {
\'<a href="',
- $_->$mode_uri->Resolver->HREF,
+ $_->$mode_uri->AsHREF,
\'">',
- ( $_->$mode_uri->IsLocal && $_->$local_type ? $_->$local_type : $_->$mode_uri->Resolver->AsString ),
+ ( $_->$mode_uri->AsString ),
\'</a><br />',
- } @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef }
+ } # if someone says __RefersTo.{Ticket}__ filter for only local links that are tickets
+ grep { $ObjectType
+ ? ( $_->$mode_uri->IsLocal
+ && ( $_->$mode_uri->Object->RecordType eq $ObjectType ))
+ : 1
+ }
+ @{ $_[0]->Links($other_mode,$type)->ItemsArrayRef }
+ }
+};
+
+my $trustSub = sub {
+ my $user = shift;
+ my %key = RT::Crypt->GetKeyInfo( Key => $user->EmailAddress );
+ if (!defined $key{'info'}) {
+ return $m->interp->apply_escapes(' ' . loc("(no pubkey!)"), "h");
+ } elsif ($key{'info'}{'TrustLevel'} == 0) {
+ return $m->interp->apply_escapes(' ' . loc("(untrusted!)"), "h");
}
};
@@ -118,7 +135,7 @@ $COLUMN_MAP = {
my $SearchURL = RT->Config->Get('WebPath') . '/Search/Results.html?' . $m->comp('/Elements/QueryString', Query => $Query);
- return \'<a href="',$SearchURL,\'">', loc('(pending [quant,_1,other ticket])',$count), \'</a>';
+ return \'<a href="',$SearchURL,\'">', loc('(pending [quant,_1,other ticket,other tickets])',$count), \'</a>';
}
}
else {
@@ -169,21 +186,6 @@ $COLUMN_MAP = {
title => 'Time Estimated', # loc
value => sub { return $_[0]->TimeEstimated }
},
- Requestors => {
- title => 'Requestors', # loc
- attribute => 'Requestor.EmailAddress',
- value => sub { return $_[0]->Requestors->MemberEmailAddressesAsString }
- },
- Cc => {
- title => 'Cc', # loc
- attribute => 'Cc.EmailAddress',
- value => sub { return $_[0]->Cc->MemberEmailAddressesAsString }
- },
- AdminCc => {
- title => 'AdminCc', # loc
- attribute => 'AdminCc.EmailAddress',
- value => sub { return $_[0]->AdminCc->MemberEmailAddressesAsString }
- },
StartsRelative => {
title => 'Starts', # loc
attribute => 'Starts',
@@ -205,7 +207,7 @@ $COLUMN_MAP = {
value => sub {
my $date = $_[0]->DueObj;
# Highlight the date if it was due in the past, and it's still active
- if ( $date && $date->Unix > 0 && $date->Diff < 0 && $_[0]->QueueObj->IsActiveStatus($_[0]->Status)) {
+ if ( $date && $date->IsSet && $date->Diff < 0 && $_[0]->QueueObj->IsActiveStatus($_[0]->Status)) {
return (\'<span class="overdue">' , $date->AgeAsString , \'</span>');
} else {
return $date->AgeAsString;
@@ -264,21 +266,7 @@ $COLUMN_MAP = {
KeyRequestors => {
title => 'Requestors', # loc
attribute => 'Requestor.EmailAddress',
- value => sub {
- my $t = shift;
- my @requestors = $t->Requestors->MemberEmailAddresses;
- for my $email (@requestors)
- {
- my %key = RT::Crypt::GnuPG::GetKeyInfo($email);
- if (!defined $key{'info'}) {
- $email .= ' ' . loc("(no pubkey!)");
- }
- elsif ($key{'info'}{'TrustLevel'} == 0) {
- $email .= ' ' . loc("(untrusted!)");
- }
- }
- return join ', ', @requestors;
- }
+ value => sub { return \($m->scomp("/Elements/ShowPrincipal", Object => $_[0]->Requestor, PostUser => $trustSub ) ) }
},
KeyOwnerName => {
title => 'Owner', # loc
@@ -286,7 +274,7 @@ $COLUMN_MAP = {
value => sub {
my $t = shift;
my $name = $t->OwnerObj->Name;
- my %key = RT::Crypt::GnuPG::GetKeyInfo($t->OwnerObj->EmailAddress);
+ my %key = RT::Crypt->GetKeyInfo( Key => $t->OwnerObj->EmailAddress );
if (!defined $key{'info'}) {
$name .= ' '. loc("(no pubkey!)");
}
@@ -297,11 +285,16 @@ $COLUMN_MAP = {
return $name;
}
},
+ KeyOwner => {
+ title => 'Owner', # loc
+ attribute => 'Owner',
+ value => sub { return \($m->scomp("/Elements/ShowPrincipal", Object => $_[0]->OwnerObj, PostUser => $trustSub ) ) }
+ },
# Everything from LINKTYPEMAP
(map {
$_ => { value => $LinkCallback->( $_ ) }
- } keys %RT::Ticket::LINKTYPEMAP),
+ } keys %RT::Link::TYPEMAP),
'_CLASS' => {
value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
@@ -328,20 +321,15 @@ $COLUMN_MAP = {
$m->comp('/Elements/CustomerFields', 'ColumnMap'),
$m->comp('/Elements/ServiceFields', 'ColumnMap'),
};
-
-# if no GPG support, then KeyOwnerName and KeyRequestors fall back to the regular
-# versions
-if (RT->Config->Get('GnuPG')->{'Enable'}) {
- require RT::Crypt::GnuPG;
-}
-else {
- $COLUMN_MAP->{KeyOwnerName} = $COLUMN_MAP->{OwnerName};
- $COLUMN_MAP->{KeyRequestors} = $COLUMN_MAP->{Requestors};
-}
</%ONCE>
<%init>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
-# backward compatibility
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap' );
+# if no encryption support, then KeyOwnerName and KeyRequestors fall back to the regular
+# versions
+unless (RT->Config->Get('Crypt')->{'Enable'}) {
+ $COLUMN_MAP->{KeyOwnerName} = $COLUMN_MAP->{OwnerName};
+ $COLUMN_MAP->{KeyRequestors} = $GenericMap->{Requestors};
+}
+
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'Once', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%init>
diff --git a/rt/share/html/Elements/RT__User/ColumnMap b/rt/share/html/Elements/RT__User/ColumnMap
index 9c3aa27..3b9877c 100644
--- a/rt/share/html/Elements/RT__User/ColumnMap
+++ b/rt/share/html/Elements/RT__User/ColumnMap
@@ -48,15 +48,10 @@
<%ARGS>
$Name
$Attr => undef
+$GenericMap => {}
</%ARGS>
<%ONCE>
my $COLUMN_MAP = {
- id => {
- title => '#', # loc
- attribute => 'id',
- align => 'right',
- value => sub { return $_[0]->id },
- },
Name => {
title => 'Name', # loc
attribute => 'Name',
@@ -147,10 +142,14 @@ my $COLUMN_MAP = {
attribute => 'FreeformContactInfo',
value => sub { return $_[0]->FreeformContactInfo() },
},
+ Disabled => {
+ title => 'Status', # loc
+ value => sub { return $_[0]->Disabled? $_[0]->loc('Disabled'): $_[0]->loc('Enabled') },
+ },
};
</%ONCE>
<%INIT>
-$m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
+$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
</%INIT>
diff --git a/rt/share/html/Elements/Refresh b/rt/share/html/Elements/Refresh
index 9a44481..655d1b8 100755
--- a/rt/share/html/Elements/Refresh
+++ b/rt/share/html/Elements/Refresh
@@ -56,7 +56,7 @@
% if ( $Default && ($value == $Default)) {
selected="selected"
% }
-><&|/l, $value/60 &>Refresh this page every [_1] minutes.</&></option>
+><&|/l, $value/60 &>Refresh this page every [quant,_1,minute,minutes].</&></option>
%}
</select>
diff --git a/rt/share/html/Elements/SelectBoolean b/rt/share/html/Elements/SelectBoolean
index 79283da..02b49e7 100755
--- a/rt/share/html/Elements/SelectBoolean
+++ b/rt/share/html/Elements/SelectBoolean
@@ -63,9 +63,9 @@ $False => loc("isn't")
my $TrueDefault = '';
my $FalseDefault = '';
if ($Default && $Default !~ /true/i) {
- $FalseDefault = 'selected="selected"';
+ $FalseDefault = 'selected="selected"';
}
else {
- $TrueDefault = 'selected="selected"';
+ $TrueDefault = 'selected="selected"';
}
</%INIT>
diff --git a/rt/share/html/Elements/SelectCustomFieldValue b/rt/share/html/Elements/SelectCustomFieldValue
index 38f0f62..02a95c2 100755
--- a/rt/share/html/Elements/SelectCustomFieldValue
+++ b/rt/share/html/Elements/SelectCustomFieldValue
@@ -45,19 +45,21 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% $m->callback( Name => $Name, CustomField => $CustomField );
+% $m->callback( Name => $Name, CustomField => $CustomField, Default => \$Default );
+% $Default = "" unless defined $Default;
% if ($CustomField->Type =~ /Select/i) {
% my $values = $CustomField->Values;
<select name="<%$Name%>">
<option value="" selected="selected">-</option>
<option value="NULL"><&|/l&>(no value)</&></option>
% while (my $value = $values->Next) {
-<option value="<%$value->Name%>"><%$value->Name%></option>
+<option value="<%$value->Name%>"<% ($value->Name eq $Default) ? q[ selected="selected"] : ''%>><%$value->Name%></option>
% }
</select>
-% }
-% elsif ( $CustomField->Type eq 'Autocomplete' ) {
-<input type="text" id="CF-<% $CustomField->id %>" name="<% $Name %>" size="20" />
+% } elsif ($CustomField->Type =~ /^Date(Time)?$/) {
+<& /Elements/SelectDate, ShowTime => ($1 ? 1 : 0), Name => $Name, Value => $Default &>
+% } elsif ( $CustomField->Type eq 'Autocomplete' ) {
+<input type="text" id="CF-<% $CustomField->id %>" name="<% $Name %>" size="20" value="<% $Default %>" />
<script type="text/javascript">
% my @options;
% my $values = $CustomField->Values;
@@ -70,9 +72,10 @@
jQuery('#'+'CF-' + <% $CustomField->id %>).autocomplete({ source: <% JSON::to_json(\@options) |n %> });
</script>
% } else {
-<input name="<%$Name%>" size="20" />
+<input name="<%$Name%>" size="20" value="<% $Default %>" type="text" />
% }
<%args>
$Name => undef
-$CustomField =>undef
+$CustomField => undef
+$Default => undef
</%args>
diff --git a/rt/share/html/Elements/SelectDate b/rt/share/html/Elements/SelectDate
index 1483527..1fd1968 100755
--- a/rt/share/html/Elements/SelectDate
+++ b/rt/share/html/Elements/SelectDate
@@ -47,7 +47,7 @@
%# END BPS TAGGED BLOCK }}}
%# in PageLayout instead, once <% include('/elements/init_calendar.html') |n %>
% $m->callback( %ARGS, Name => $Name, CallbackName => 'BeforeDateInput' );
-<input type="text" class="ui-datepicker<% $ShowTime ? ' withtime' : '' %>" id="<% $Name %>" name="<% $Name %>" value="<% $Value %>" size="<% $Size %>" />
+<input type="text" class="datepicker<% $ShowTime ? ' withtime' : '' %>" id="<% $Name %>" name="<% $Name %>" value="<% $Value %>" size="<% $Size %>" />
<IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $Name %>_date_button" STYLE="cursor: pointer" TITLE="Select date">
% $m->callback( %ARGS, Name => $Name, CallbackName => 'AfterDateInput' );
<script type="text/javascript">
@@ -63,23 +63,21 @@ Calendar.setup({
});
</script>
<%init>
-unless ((defined $Default) or
- ($current <= 0)) {
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
- localtime($current);
- $Default = sprintf("%04d-%02d-%02d %02d:%02d",
- $year+1900,$mon+1,$mday,
- $hour,$min);
+unless ((defined $Default) or ($current <= 0)) {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ localtime($current);
+ $Default = sprintf("%04d-%02d-%02d %02d:%02d",
+ $year+1900,$mon+1,$mday,
+ $hour,$min);
}
$Value = $Value || $Default;
unless ($Name) {
- $Name = $menu_prefix. "_Date";
+ $Name = $menu_prefix. "_Date";
}
</%init>
<%args>
-
$ShowTime => 1
$menu_prefix=>''
$current=>time
diff --git a/rt/share/html/Elements/SelectDateRelation b/rt/share/html/Elements/SelectDateRelation
index 6c662fa..74ae9a1 100755
--- a/rt/share/html/Elements/SelectDateRelation
+++ b/rt/share/html/Elements/SelectDateRelation
@@ -55,6 +55,6 @@
$Name => undef
$Default => undef
$Before => loc('before')
-$On => loc('on')
+$On => loc('on')
$After => loc('after')
</%ARGS>
diff --git a/rt/share/html/Elements/SelectLang b/rt/share/html/Elements/SelectLang
index 9ba09f8..cf4d297 100755
--- a/rt/share/html/Elements/SelectLang
+++ b/rt/share/html/Elements/SelectLang
@@ -59,11 +59,9 @@
</select>
<%ARGS>
$ShowNullOption => 1
-$ShowAllQueues => 1
$Name => undef
$Verbose => undef
$Default => 0
-$Lite => 0
</%ARGS>
<%ONCE>
diff --git a/rt/share/html/Elements/SelectMatch b/rt/share/html/Elements/SelectMatch
index 65e108d..d76cfbb 100755
--- a/rt/share/html/Elements/SelectMatch
+++ b/rt/share/html/Elements/SelectMatch
@@ -68,15 +68,15 @@ my $LikeDefault='';
my $NotLikeDefault ='';
if ($Default && $Default =~ /false|!=/i) {
- $FalseDefault = qq[ selected="selected"];
+ $FalseDefault = qq[ selected="selected"];
}
elsif ($Default && $Default =~ /true|=/i) {
- $TrueDefault = qq[ selected="selected"];
-}
+ $TrueDefault = qq[ selected="selected"];
+}
elsif ($Default && $Default =~ /notlike|NOT LIKE/i) {
- $NotLikeDefault = qq[ selected="selected"];
+ $NotLikeDefault = qq[ selected="selected"];
}
else {
- $LikeDefault = qq[ selected="selected"];
+ $LikeDefault = qq[ selected="selected"];
}
</%INIT>
diff --git a/rt/share/html/Elements/SelectObject b/rt/share/html/Elements/SelectObject
new file mode 100644
index 0000000..5952ce2
--- /dev/null
+++ b/rt/share/html/Elements/SelectObject
@@ -0,0 +1,141 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+% if ($Lite) {
+% my $d = $ObjectType->new($session{'CurrentUser'});
+% $d->Load($Default);
+<input name="<%$Name%>" size="25" value="<%$d->Name%>" class="<%$Class%>" />
+% }
+% else {
+<select name="<%$Name%>" <% ($Multiple) ? qq{multiple="multiple" size="$Size"} : '' |n%> <% ($OnChange) ? 'onchange="'.$OnChange.'"' : '' |n %> class="<%$Class%>">
+% if ($ShowNullOption) {
+ <option value=""><% $DefaultLabel %></option>
+% }
+% for my $object (@{$session{$cache_key}{objects}}) {
+ <option value="<% ($NamedValues ? $object->{Name} : $object->{Id}) %>"\
+% if ($object->{Id} eq ($Default||'') || $object->{Name} eq ($Default||'')) {
+ selected="selected"\
+% }
+><%$object->{Name}%>\
+% if ($Verbose and $object->{Description}) {
+ (<%$object->{Description}%>)\
+% }
+</option>
+% }
+</select>
+% }
+<%args>
+$ObjectType
+$CheckRight => undef
+$ShowNullOption => 1
+$ShowAll => 1
+$Name => undef
+$Verbose => undef
+$NamedValues => 0
+$DefaultLabel => "-"
+$Default => 0
+$Lite => 0
+$OnChange => undef
+$Multiple => 0
+$Size => 6
+$Class => ""
+$CacheNeedsUpdate => undef
+</%args>
+<%init>
+$ObjectType = "RT::$ObjectType" unless $ObjectType =~ /::/;
+$Class ||= "select-" . CSSClass("\L$1") if $ObjectType =~ /RT::(.+)$/;
+
+my $cache_key = join "---", "SelectObject", $ObjectType,
+ $session{'CurrentUser'}->Id, $CheckRight || "", $ShowAll;
+
+if ( defined $session{$cache_key} && ref $session{$cache_key} eq 'ARRAY') {
+ delete $session{$cache_key};
+}
+if ( defined $session{$cache_key} && defined $CacheNeedsUpdate &&
+ $session{$cache_key}{lastupdated} <= $CacheNeedsUpdate ) {
+ delete $session{$cache_key};
+}
+
+if ( not defined $session{$cache_key} and not $Lite ) {
+ my $collection = "${ObjectType}s"->new($session{'CurrentUser'});
+ $collection->UnLimit;
+
+ $m->callback( CallbackName => 'ModifyCollection', ARGSRef => \%ARGS,
+ Collection => $collection, ObjectType => $ObjectType );
+
+ if ( $Default ) {
+ my $object = $ObjectType->new($session{'CurrentUser'});
+ $object->Load( $Default );
+ unless ( $ShowAll
+ or not $CheckRight
+ or $session{CurrentUser}->HasRight( Object => $object, Right => $CheckRight ) )
+ {
+ if ( $object->id ) {
+ push @{$session{$cache_key}{objects}}, {
+ Id => $object->id,
+ Name => '#' . $object->id,
+ Description => '#' . $object->id,
+ };
+ }
+ }
+ }
+
+ while (my $object = $collection->Next) {
+ if ($ShowAll
+ or not $CheckRight
+ or $session{CurrentUser}->HasRight( Object => $object, Right => $CheckRight ))
+ {
+ push @{$session{$cache_key}{objects}}, {
+ Id => $object->Id,
+ Name => $object->Name,
+ Description => $object->_Accessible("Description" => "read") ? $object->Description : undef,
+ };
+ }
+ }
+ $session{$cache_key}{lastupdated} = time();
+}
+</%init>
diff --git a/rt/share/html/Elements/SelectOwnerAutocomplete b/rt/share/html/Elements/SelectOwnerAutocomplete
index d54bce3..95c0f2c 100644
--- a/rt/share/html/Elements/SelectOwnerAutocomplete
+++ b/rt/share/html/Elements/SelectOwnerAutocomplete
@@ -86,7 +86,7 @@ my $query = $m->comp('/Elements/QueryString',
}
else {
jQuery.ajax({
- url: <% RT->Config->Get('WebPath')|n,j%>+"/Helpers/Autocomplete/Owners?"+<% $query|n,j %>,
+ url: RT.Config.WebPath + "/Helpers/Autocomplete/Owners?"+<% $query|n,j %>,
dataType: "json",
data: request,
success: function( data ) {
diff --git a/rt/share/html/Elements/SelectOwnerDropdown b/rt/share/html/Elements/SelectOwnerDropdown
index acd8bc9..60e9cde 100644
--- a/rt/share/html/Elements/SelectOwnerDropdown
+++ b/rt/share/html/Elements/SelectOwnerDropdown
@@ -49,10 +49,6 @@
%if ($DefaultValue) {
<option value=""<% !$Default ? qq[ selected="selected"] : '' |n %>><%$DefaultLabel |n%></option>
%}
-% $Default = 0 unless defined $Default && $Default =~ /^\d+$/;
-% my @formatednames = sort {lc $a->[1] cmp lc $b->[1]} map {[$_, $m->scomp('/Elements/ShowUser', User => $_)]} grep { $_->id != RT->Nobody->id } @users;
-% my $nobody = [RT->Nobody, $m->scomp('/Elements/ShowUser', User => RT->Nobody)];
-% unshift @formatednames, $nobody;
%foreach my $UserRef ( @formatednames) {
%my $User = $UserRef->[0];
<option <% ( $User->Id == $Default) ? qq[ selected="selected"] : '' |n %>
@@ -61,7 +57,7 @@
%} elsif ($ValueAttribute eq 'Name') {
value="<%$User->Name%>"
%}
-><% $UserRef->[1] |n %></option>
+><% $UserRef->[1] %></option>
%}
</select>
<%INIT>
@@ -71,6 +67,7 @@ my $isSU = $session{CurrentUser}
->HasRight( Right => 'SuperUser', Object => $RT::System );
foreach my $object (@$Objects) {
my $Users = RT::Users->new( $session{CurrentUser} );
+ $Users->LimitToPrivileged;
$Users->WhoHaveRight(
Right => 'OwnTicket',
Object => $object,
@@ -81,12 +78,35 @@ foreach my $object (@$Objects) {
$user_uniq_hash{ $User->Id() } = $User;
}
}
+
+my $dropdown_limit = 50;
+$m->callback( CallbackName => 'ModifyDropdownLimit', DropdownLimit => \$dropdown_limit );
+
+if (keys(%user_uniq_hash) > $dropdown_limit ) {
+ if ($Objects->[0]->id) {
+ my $desc = $Objects->[0]->RecordType." ".$Objects->[0]->id;
+ RT->Logger->notice("More than $dropdown_limit possible Owners found for $desc; switching to autocompleter. See the \$AutocompleteOwners configuration option");
+ }
+ $m->comp("/Elements/SelectOwnerAutocomplete", %ARGS);
+ return;
+}
+
if ($Default && $Default != RT->Nobody->id && !$user_uniq_hash{$Default}) {
$user_uniq_hash{$Default} = RT::User->new($session{CurrentUser});
$user_uniq_hash{$Default}->Load($Default);
}
+$Default = 0 unless defined $Default && $Default =~ /^\d+$/;
+
+my @formatednames = sort {lc $a->[1] cmp lc $b->[1]}
+ map {[$_, $_->Format]}
+ grep { $_->id != RT->Nobody->id }
+ values %user_uniq_hash;
+
+my $nobody_user = RT::User->new( $session{CurrentUser} );
+$nobody_user->Load( RT->Nobody->id );
+my $nobody = [$nobody_user, $nobody_user->Format];
+unshift @formatednames, $nobody;
-my @users = values %user_uniq_hash;
</%INIT>
<%ARGS>
diff --git a/rt/share/html/Elements/SelectQueue b/rt/share/html/Elements/SelectQueue
index 76440d1..ae645a7 100755
--- a/rt/share/html/Elements/SelectQueue
+++ b/rt/share/html/Elements/SelectQueue
@@ -45,92 +45,14 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% if ($Lite) {
-% my $d = RT::Queue->new($session{'CurrentUser'});
-% $d->Load($Default);
-<input name="<%$Name%>" size="25" value="<%$d->Name%>" class="<%$Class%>" />
-% }
-% else {
-<select name="<%$Name%>" <% ($Multiple) ? qq{multiple="multiple" size="$Size"} : '' |n%> <% ($OnChange) ? 'onchange="'.$OnChange.'"' : '' |n %> class="<%$Class%>">
-% if ($ShowNullOption) {
- <option value=""><% $DefaultLabel %></option>
-% }
-% for my $queue (@{$session{$cache_key}{queues}}) {
- <option value="<% ($NamedValues ? $queue->{Name} : $queue->{Id}) %>"\
-% if ($queue->{Id} eq ($Default||'') || $queue->{Name} eq ($Default||'')) {
- selected="selected"\
-% }
-><%$queue->{Name}%>\
-% if ($Verbose and $queue->{Description}) {
- (<%$queue->{Description}%>)\
-% }
-</option>
-% }
-</select>
-% }
+<& SelectObject,
+ %ARGS,
+ ObjectType => "Queue",
+ CheckRight => $CheckQueueRight,
+ ShowAll => $ShowAllQueues,
+ CacheNeedsUpdate => RT->System->QueueCacheNeedsUpdate,
+ &>
<%args>
$CheckQueueRight => 'CreateTicket'
-$ShowNullOption => 1
$ShowAllQueues => 1
-$Name => undef
-$Verbose => undef
-$NamedValues => 0
-$DefaultLabel => "-"
-$Default => 0
-$Lite => 0
-$OnChange => undef
-$Multiple => 0
-$Size => 6
-$Class => 'select-queue'
</%args>
-<%init>
-my $cache_key = "SelectQueue---"
- . $session{'CurrentUser'}->Id
- . "---$CheckQueueRight---$ShowAllQueues";
-
-if ( defined $session{$cache_key} && ref $session{$cache_key} eq 'ARRAY') {
- delete $session{$cache_key};
-}
-if ( defined $session{$cache_key} &&
- $session{$cache_key}{lastupdated} <= RT->System->QueueCacheNeedsUpdate ) {
- delete $session{$cache_key};
-}
-
-if ( defined $session{$cache_key} && ref $session{$cache_key} eq 'ARRAY') {
- delete $session{$cache_key};
-}
-if ( defined $session{$cache_key} &&
- $session{$cache_key}{lastupdated} <= RT->System->QueueCacheNeedsUpdate ) {
- delete $session{$cache_key};
-}
-
-if ( not defined $session{$cache_key} and not $Lite ) {
- my $q = RT::Queues->new($session{'CurrentUser'});
- $q->UnLimit;
-
- if ( $Default ) {
- my $d = RT::Queue->new($session{'CurrentUser'});
- $d->Load($Default);
- unless ( $d->CurrentUserHasRight('SeeQueue') ) {
- if ( $d->id ) {
- push @{$session{$cache_key}{queues}}, {
- Id => $d->id,
- Name => '#' . $d->id,
- Description => '#' . $d->id,
- };
- }
- }
- }
-
- while (my $queue = $q->Next) {
- if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
- push @{$session{$cache_key}{queues}}, {
- Id => $queue->Id,
- Name => $queue->Name,
- Description => $queue->Description,
- };
- }
- }
- $session{$cache_key}{lastupdated} = time();
-}
-</%init>
diff --git a/rt/share/html/Elements/SelectStatus b/rt/share/html/Elements/SelectStatus
index 3c833d8..faae3dc 100755
--- a/rt/share/html/Elements/SelectStatus
+++ b/rt/share/html/Elements/SelectStatus
@@ -70,39 +70,35 @@
my %statuses_by_lifecycle;
if ( @Statuses ) {
$statuses_by_lifecycle{''} = \@Statuses;
-}
-elsif ( $TicketObj ) {
- my @status;
- my $current = $TicketObj->Status;
- push @status, $current;
-
- my $lifecycle = $TicketObj->QueueObj->Lifecycle;
+} else {
+ if ( $Object ) {
+ my $lifecycle = $Object->LifecycleObj;
+ if ($Object->_Accessible("Status", "read")) {
+ my $current = $Object->Status;
+ my @status;
+ push @status, $current;
- my %has = ();
- foreach my $next ( $lifecycle->Transitions( $current ) ) {
- my $check = $lifecycle->CheckRight( $current => $next );
- $has{ $check } = $TicketObj->CurrentUserHasRight( $check )
- unless exists $has{ $check };
- push @status, $next if $has{ $check };
+ my %has = ();
+ foreach my $next ( $lifecycle->Transitions( $current ) ) {
+ my $check = $lifecycle->CheckRight( $current => $next );
+ $has{ $check } = $Object->CurrentUserHasRight( $check )
+ unless exists $has{ $check };
+ push @status, $next if $has{ $check };
+ }
+ $statuses_by_lifecycle{$lifecycle->Name} = \@status;
+ } else {
+ $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Transitions('') ];
+ }
}
- $statuses_by_lifecycle{$lifecycle->Name} = \@status;
-}
-elsif ( $QueueObj ) {
- my $lifecycle = $QueueObj->Lifecycle;
- $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Transitions('') ];
-} elsif ( %Queues ) {
- for my $id (keys %Queues) {
- my $queue = RT::Queue->new($session{'CurrentUser'});
- $queue->Load($id);
- if ($queue->id) {
- my $lifecycle = $queue->Lifecycle;
+ for my $lifecycle ( @Lifecycles ) {
+ $statuses_by_lifecycle{$lifecycle->Name} ||= [ $lifecycle->Valid ];
+ }
+
+ if (not keys %statuses_by_lifecycle) {
+ for my $lifecycle (map { RT::Lifecycle->Load($_) } RT::Lifecycle->List($Type)) {
$statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ];
}
}
-} else {
- for my $lifecycle (map { RT::Lifecycle->Load($_) } RT::Lifecycle->List) {
- $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ];
- }
}
if (keys %statuses_by_lifecycle) {
@@ -132,11 +128,11 @@ my $group_by_lifecycle = keys %statuses_by_lifecycle > 1;
</%INIT>
<%ARGS>
$Name => undef
+$Type => undef,
@Statuses => ()
-$TicketObj => undef
-$QueueObj => undef
-%Queues => ()
+$Object => undef,
+@Lifecycles => (),
$Default => ''
$SkipDeleted => 0
diff --git a/rt/share/html/Elements/SelectTicketTypes b/rt/share/html/Elements/SelectTicketTypes
deleted file mode 100755
index cecf617..0000000
--- a/rt/share/html/Elements/SelectTicketTypes
+++ /dev/null
@@ -1,58 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
-%# <sales@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<select name="<%$Name%>">
-%foreach (@Types) {
-<option value="<% $_ %>"<% ($_ eq $Default) && qq[ selected="selected"] |n %>><% loc($_) %>
-%}
-</select>
-
-<%ARGS>
-$Name => 'TickType'
-$Default => undef
-@Types => qw(Approval Ticket)
-</%ARGS>
diff --git a/rt/share/html/Elements/SelectTimeUnits b/rt/share/html/Elements/SelectTimeUnits
index 660e5c7..da5376b 100755
--- a/rt/share/html/Elements/SelectTimeUnits
+++ b/rt/share/html/Elements/SelectTimeUnits
@@ -46,17 +46,17 @@
%#
%# END BPS TAGGED BLOCK }}}
<select class="TimeUnits" id="<% $Name %>" name="<% $Name %>">
-<option value="minutes" <% $HoursDefault ? '' : 'selected="selected"' |n%>>
+<option value="minutes" <% $Default eq 'minutes' ? 'selected="selected"' : '' |n%>>
<% loc('Minutes') %>
</option>
-<option value="hours" <% $HoursDefault ? 'selected="selected"' : '' |n%>>
+<option value="hours" <% $Default eq 'hours' ? 'selected="selected"' : '' |n%>>
<% loc('Hours') %>
</option>
</select>
<%INIT>
$Name .= '-TimeUnits' unless $Name =~ /-TimeUnits$/io;
-my $HoursDefault = RT->Config->Get('DefaultTimeUnitsToHours', $session{'CurrentUser'});
</%INIT>
<%ARGS>
$Name => ''
+$Default => RT->Config->Get('DefaultTimeUnitsToHours', $session{'CurrentUser'}) ? 'hours' : 'minutes'
</%ARGS>
diff --git a/rt/share/html/Elements/SelectSortOrder b/rt/share/html/Elements/ShowCustomFieldCustomGroupings
index 0075df6..dfce725 100755..100644
--- a/rt/share/html/Elements/SelectSortOrder
+++ b/rt/share/html/Elements/ShowCustomFieldCustomGroupings
@@ -45,21 +45,34 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<select name="<%$Name%>">
-%foreach my $order (@orders) {
-<option value="<%$order%>"<%$order eq $Default && qq[ selected="selected"] |n %>>
-<% shift @order_names %>
-</option>
+<%perl>
+for my $group ( @Groupings ) {
+ my %grouping_args = (
+ title => $group? loc($group) : loc('Custom Fields'),
+ class => $css_class .' '. ($group? CSSClass("$css_class-$group") : ''),
+ hide_empty => 1,
+ title_href => $title_href ? "$title_href?id=".$Object->id.($group?";Grouping=".$m->interp->apply_escapes($group,'u')."#".CSSClass("$css_class-$group") : "#".$css_class) : undef,
+ %$TitleBoxARGS,
+ );
+ $m->callback( CallbackName => 'TitleBox', Object => $Object, Grouping => $group, ARGSRef => \%grouping_args );
+</%perl>
+<&| /Widgets/TitleBox, %grouping_args &>
+<& ShowCustomFields, %ARGS, Object => $Object, Grouping => $group &>
+</&>
% }
-</select>
-
+<%ARGS>
+$Object
+$title_href => ""
+@Groupings => ()
+</%ARGS>
<%INIT>
-my @orders = qw (ASC DESC);
-my @order_names = (loc('Ascending'), loc('Descending'));
+my $css_class = lc(ref($Object)||$Object);
+$css_class =~ s/^rt:://;
+$css_class =~ s/::/-/g;
+$css_class = CSSClass($css_class);
+$css_class .= '-info-cfs';
-</%INIT>
+my $TitleBoxARGS = delete $ARGS{TitleBoxARGS} || {};
-<%ARGS>
-$Name => 'SortOrder'
-$Default => 'ASC'
-</%ARGS>
+@Groupings = (RT::CustomField->CustomGroupings( $Object ), '') unless @Groupings;
+</%INIT>
diff --git a/rt/share/html/Elements/ShowCustomFieldImage b/rt/share/html/Elements/ShowCustomFieldImage
index 6df80cc..f5a1886 100644
--- a/rt/share/html/Elements/ShowCustomFieldImage
+++ b/rt/share/html/Elements/ShowCustomFieldImage
@@ -46,7 +46,7 @@
%#
%# END BPS TAGGED BLOCK }}}
% my $url = RT->Config->Get('WebPath') . "/Download/CustomFieldValue/".$Object->Id.'/'.$m->interp->apply_escapes($Object->Content, 'u');
-<a href="<% $url %>"><% $Object->Content %></a>
+<a href="<% $url %>"><% $Object->Content %></a><br>
<img type="<% $Object->ContentType %>" height="64" src="<% $url %>" align="middle" />
<%ARGS>
$Object
diff --git a/rt/share/html/Elements/ShowCustomFields b/rt/share/html/Elements/ShowCustomFields
index 75e302b..d8e6acf 100644
--- a/rt/share/html/Elements/ShowCustomFields
+++ b/rt/share/html/Elements/ShowCustomFields
@@ -45,7 +45,8 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-% $m->callback( CallbackName => 'BeforeCustomFields' );
+% $m->callback( CallbackName => 'BeforeCustomFields', Object => $Object,
+% Grouping => $Grouping, ARGSRef => \%ARGS, CustomFields => $CustomFields, Table => $Table );
% if ($Table) {
<table>
% }
@@ -54,6 +55,7 @@
% while ( my $CustomField = $set->Next ) {
% my $Values = $Object->CustomFieldValues( $CustomField->Id );
% my $count = $Values->Count;
+% next if $HideEmpty and not $count;
% next if $count == 0 and $CustomField->Disabled;
<tr id="CF-<%$CustomField->id%>-ShowRow">
<td class="label"><% $CustomField->Name %>:</td>
@@ -72,19 +74,23 @@
</ul>
% }
</td>
+% $m->callback( CallbackName => 'AfterCustomFieldValue', CustomField => $CustomField,
+% Object => $Object, Grouping => $Grouping, Table => $Table );
</tr>
% }
% }
% if ($Table) {
</table>
% }
-% $m->callback( CallbackName => 'AfterCustomFields', Object => $Object );
+% $m->callback( CallbackName => 'AfterCustomFields', Object => $Object,
+% Grouping => $Grouping, ARGSRef => \%ARGS, Table => $Table );
<%INIT>
$m->callback(
%ARGS,
CallbackName => 'MassageCustomFields',
- Object => $Object,
+ Object => $Object,
CustomFields => $CustomFields,
+ Table => $Table,
);
# kludge to allow "Support time" to be displayed even though it's been
@@ -94,6 +100,8 @@ $HiddenCustomFields->LimitToChildType(ref $Object);
$HiddenCustomFields->Limit( FIELD => 'Type', VALUE => 'TimeValue' );
$HiddenCustomFields->LimitToDeleted;
+$CustomFields->LimitToGrouping( $Object => $Grouping ) if defined $Grouping;
+
# don't print anything if there is no custom fields
return unless $CustomFields->Count > 0 or $HiddenCustomFields->Count > 0;
@@ -102,7 +110,7 @@ my $print_value = sub {
my $linked = $value->LinkValueTo;
if ( defined $linked && length $linked ) {
my $linked = $m->interp->apply_escapes( $linked, 'h' );
- $m->out('<a href="'. $linked .'" target="_new">');
+ $m->out('<a href="'. $linked .'" target="_blank">');
}
my $comp = "ShowCustomField". $cf->Type;
$m->callback(
@@ -110,6 +118,7 @@ my $print_value = sub {
Name => \$comp,
CustomField => $cf,
Object => $Object,
+ Table => $Table,
);
if ( $m->comp_exists( $comp ) ) {
$m->comp( $comp, Object => $value );
@@ -137,6 +146,7 @@ my $print_value = sub {
<%ARGS>
$Object => undef
$CustomFields => $Object->CustomFields
-
+$Grouping => undef
$Table => 1
+$HideEmpty => 0
</%ARGS>
diff --git a/rt/share/html/Elements/ShowHistory b/rt/share/html/Elements/ShowHistory
new file mode 100644
index 0000000..d585453
--- /dev/null
+++ b/rt/share/html/Elements/ShowHistory
@@ -0,0 +1,191 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<div class="history <% lc $record_type %>" id="<% $histid %>">
+<%perl>
+if ( $ShowDisplayModes or $ShowTitle ) {
+ my $title = $ShowTitle
+ ? loc('History')
+ : '&nbsp;';
+
+ my $titleright = '';
+ if ( $ShowDisplayModes ) {
+ if ( RT->Config->Get( 'QuoteFolding', $session{CurrentUser} ) ) {
+ my $open_all = $m->interp->apply_escapes( loc("Show all quoted text"), 'j' );
+ my $open_html = $m->interp->apply_escapes( loc("Show all quoted text"), 'h' );
+ my $close_all = $m->interp->apply_escapes( loc("Hide all quoted text"), 'j' );
+ $titleright .= '<a href="#" data-direction="open" '
+ . qq{onclick="return toggle_all_folds(this, $open_all, $close_all);"}
+ . ">$open_html</a> &mdash; ";
+ }
+
+ if ($ShowHeaders) {
+ $titleright .= qq{<a href="?ForceShowHistory=1;id=} .
+ $Object->id.qq{#$histid">} .
+ loc("Show brief headers") .
+ qq{</a>};
+ } else {
+ $titleright .= qq{<a href="?ForceShowHistory=1;ShowHeaders=1;id=} .
+ $Object->id.qq{#$histid">} .
+ loc("Show full headers") .
+ qq{</a>};
+ }
+ }
+</%perl>
+<& /Widgets/TitleBoxStart, title => $title, titleright_raw => $titleright &>
+% }
+
+<div class="history-container">
+<%perl>
+$m->callback( %ARGS, Object => $Object, CallbackName => 'BeforeTransactions' );
+my $i = 1;
+while ( my $Transaction = $Transactions->Next ) {
+ my $skip = 0;
+
+ # Skip display of SetWatcher transactions for ticket Owner groups. Owner
+ # was a single member role group and denormalized into a column well before
+ # the generic role group handling and transactions came about. For
+ # tickets, we rely on rendering ownership changes using the Set-Owner
+ # transaction. For all other record types, or even potential ticket single
+ # role groups which aren't Owner, we use SetWatcher to render history and
+ # skip the Set transactions. This complication is necessary to avoid
+ # creating backdated transactions on upgrade which normalize to one type or
+ # another.
+ #
+ # These conditions assumes ticket Owner is a single-member denormalized
+ # role group, which is safe since that is unlikely to ever change in the
+ # future.
+ if ($Object->isa("RT::Ticket") and ($Transaction->Field || '') eq "Owner") {
+ $skip = 1 if $Transaction->Type eq "SetWatcher";
+ } else {
+ $skip = 1 if $Transaction->Type eq "Set"
+ and $Transaction->Field
+ and $Object->DOES("RT::Record::Role::Roles")
+ and $Object->HasRole( $Transaction->Field )
+ and $Object->RoleGroup( $Transaction->Field )->SingleMemberRoleGroupColumn;
+ }
+
+ $m->callback(
+ %ARGS,
+ Transaction => $Transaction,
+ skip => \$skip,
+ CallbackName => 'SkipTransaction',
+ );
+ next if $skip;
+
+ # ARGS is first because we're clobbering the "Attachments" parameter
+ $m->comp( 'ShowTransaction',
+ %ARGS,
+ Object => $Object,
+ Transaction => $Transaction,
+ ShowHeaders => $ShowHeaders,
+ RowNum => $i,
+ Attachments => $trans_attachments->{$Transaction->id} || {},
+ AttachmentContent => $trans_content,
+ HasTxnCFs => $HasTxnCFs,
+ );
+
+ # manually flush the content buffer after each txn,
+ # so the user sees some update
+ $m->flush_buffer;
+
+ $i++;
+}
+
+</%perl>
+</div>
+% if ($ShowDisplayModes or $ShowTitle) {
+<& /Widgets/TitleBoxEnd &>
+% }
+</div>
+<%INIT>
+my $trans_content = {};
+my $trans_attachments = {};
+
+for my $content (@{$AttachmentContent->ItemsArrayRef()}) {
+ $trans_content->{$content->TransactionId}->{$content->Id} = $content;
+}
+
+for my $attachment (@{$Attachments->ItemsArrayRef()}) {
+ my $tmp = $trans_attachments->{ $attachment->TransactionId } ||= {};
+ push @{ $tmp->{ $attachment->Parent || 0 } ||= [] }, $attachment;
+}
+
+{
+ my %tmp = (
+ DisplayPath => 'Display.html',
+ AttachmentPath => 'Attachment',
+ UpdatePath => 'Update.html',
+ ForwardPath => 'Forward.html',
+ EmailRecordPath => 'ShowEmailRecord.html',
+ EncryptionPath => 'Crypt.html',
+ );
+
+ my $prefix = $ARGS{PathPrefix}||'';
+ while ( my ($arg, $path) = each %tmp ) {
+ next if defined $ARGS{ $arg };
+ $ARGS{ $arg } = $prefix.$path;
+ }
+}
+
+my $record_type = $Object->RecordType;
+my $histid = "\L$record_type\E-" . $Object->id . "-history";
+
+my $HasTxnCFs = ($Object->can("TransactionCustomFields") and $Object->TransactionCustomFields->Count);
+</%INIT>
+<%ARGS>
+$Object
+$Transactions => $Object->SortedTransactions
+$Attachments => $Object->Attachments( WithHeaders => 1 )
+$AttachmentContent => $Object->TextAttachments
+
+$ShowHeaders => 0
+$ShowTitle => 1
+$ShowDisplayModes => 1
+
+$PathPrefix => ''
+</%ARGS>
diff --git a/rt/share/html/Elements/ShowLink b/rt/share/html/Elements/ShowLink
index b533c83..d1629b1 100644
--- a/rt/share/html/Elements/ShowLink
+++ b/rt/share/html/Elements/ShowLink
@@ -45,33 +45,26 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<a href="<% $href %>">
-% if ($URI->IsLocal) {
% my $member = $URI->Object;
-% my $has_name = UNIVERSAL::can($member, 'Name') || (UNIVERSAL::can($member, '_Accessible') && $member->_Accessible('Name', 'read'));
-% if (UNIVERSAL::isa($member, "RT::Ticket") and $member->CurrentUserHasRight('ShowTicket')) {
-% my $inactive = $member->QueueObj->IsInactiveStatus($member->Status);
+% if (blessed($member) and $member->isa("RT::Ticket") and $member->CurrentUserHasRight('ShowTicket')) {
+% my $class = $member->QueueObj->IsInactiveStatus($member->Status)
+% ? 'ticket-inactive'
+% : 'ticket-active';
+% $class .= ' '.CSSClass($member->Status);
-<span class="<% $inactive ? 'ticket-inactive' : '' %>">
-
-<%$member->Id%>: (<& /Elements/ShowUser, User => $member->OwnerObj &>) <%$member->Subject || ''%> [<% loc($member->Status) %>]
+<span class="<% $class %>">
+<a href="<% $href %>"><%$member->Id%>: <%$member->Subject || ''%> [<% loc($member->Status) %>]</a> (<& /Elements/ShowUser, User => $member->OwnerObj &>)
</span>
-% } elsif ($has_name) {
-<%$URI->Resolver->AsString%>: <%$member->Name%>
-% } else {
-<%$URI->Resolver->AsString%>
-% }
% } else {
-<%$URI->Resolver->AsString%>
+<a href="<% $href %>"><%$URI->AsString%></a>
% }
-</a>
<%ARGS>
$URI => undef
</%ARGS>
<%INIT>
-my $href = $URI->Resolver->HREF;
+my $href = $URI->AsHREF;
if ( $URI->IsLocal ) {
my $base = RT->Config->Get('WebBaseURL');
# URI->rel doesn't contain the leading '/'
diff --git a/rt/share/html/Elements/ShowLinks b/rt/share/html/Elements/ShowLinks
index 8880224..30dff00 100755
--- a/rt/share/html/Elements/ShowLinks
+++ b/rt/share/html/Elements/ShowLinks
@@ -46,134 +46,63 @@
%#
%# END BPS TAGGED BLOCK }}}
<table>
+% for my $type (@display) {
<tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depends on'), Relation => 'DependsOn' &>:\
-% if ($can_create) {
- <span class="create">(<a href="<%$clone->{'DependsOn-new'}%>"><% loc('Create') %></a>)</span>
-% }
- </td>
- <td class="value">
-<%PERL>
-my ( $depends_on, @active, @inactive, @not_tickets );
-$depends_on = $Ticket->DependsOn;
-
-while ( my $link = $depends_on->Next ) {
- my $target = $link->TargetObj;
- if ( $target && $target->isa('RT::Ticket') ) {
- if ( $target->QueueObj->IsInactiveStatus( $target->Status ) ) {
- push( @inactive, $link->TargetURI );
- }
- else {
- push( @active, $link->TargetURI );
- }
- }
- else {
- push( @not_tickets, $link->TargetURI );
- }
-}
-</%PERL>
-<ul>
-% for my $Link (@not_tickets, @active, @inactive) {
-<li><& ShowLink, URI => $Link &></li>
-% }
-</ul>
- </td>
- </tr>
- <tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Depended on by'), Relation => 'DependedOnBy' &>:\
-% if ($can_create) {
- <span class="create">(<a href="<%$clone->{'new-DependsOn'}%>"><% loc('Create') %></a>)</span>
-% }
- </td>
- <td class="value">
-<ul>
-% while (my $Link = $Ticket->DependedOnBy->Next) {
-<li><& ShowLink, URI => $Link->BaseURI &></li>
-% }
-</ul>
- </td>
- </tr>
- <tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Parents'), Relation => 'Parents' &>:\
-% if ($can_create) {
- <span class="create">(<a href="<%$clone->{'MemberOf-new'}%>"><% loc('Create') %></a>)</span>
-% }
- </td>
- <td class="value"><& /Ticket/Elements/ShowParents, Ticket => $Ticket &></td>
- </tr>
- <tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Children'), Relation => 'Children' &>:\
-% if ($can_create) {
- <span class="create">(<a href="<%$clone->{'new-MemberOf'}%>"><% loc('Create') %></a>)</span>
-% }
- </td>
- <td class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></td>
- </tr>
- <tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Refers to'), Relation => 'RefersTo' &>:\
-% if ($can_create) {
- <span class="create">(<a href="<%$clone->{'RefersTo-new'}%>"><% loc('Create') %></a>)</span>
-% }
+ <td class="labeltop">
+ <& ShowRelationLabel, Object => $Object, Label => $labels{$type}.':', Relation => $type &>
+% if ($clone{$type}) {
+ <span class="create">(<a href="<% $clone{$type} %>"><% loc('Create') %></a>)</span>
+% }
</td>
<td class="value">
-<ul>
-% while (my $Link = $Ticket->RefersTo->Next) {
-<li><& ShowLink, URI => $Link->TargetURI &></li>
-% }
-</ul>
+ <& ShowLinksOfType, Object => $Object, Type => $type, Recurse => ($type eq 'Members') &>
</td>
</tr>
- <tr>
- <td class="labeltop"><& ShowRelationLabel, id => $id, Label => loc('Referred to by'), Relation => 'ReferredToBy' &>:\
-% if ($can_create) {
- <span class="create">(<a href="<%$clone->{'new-RefersTo'}%>"><% loc('Create') %></a>)</span>
% }
- </td>
- <td class="value">
- <ul>
-% while (my $Link = $Ticket->ReferredToBy->Next) {
-% next if (UNIVERSAL::isa($Link->BaseObj, 'RT::Ticket') && $Link->BaseObj->Type eq 'reminder');
-<li><& ShowLink, URI => $Link->BaseURI &></li>
-% }
-</ul>
- </td>
- </tr>
% # Allow people to add more rows to the table
% $m->callback( %ARGS );
+ <& /Elements/ShowCustomFields, Object => $Object, Grouping => 'Links', Table => 0 &>
</table>
<%INIT>
+my @display = qw(DependsOn DependedOnBy MemberOf Members RefersTo ReferredToBy);
+$m->callback( %ARGS, CallbackName => 'ChangeDisplay', display => \@display );
+my %labels = (
+ DependsOn => loc('Depends on'),
+ DependedOnBy => loc('Depended on by'),
+ MemberOf => loc('Parents'),
+ Members => loc('Children'),
+ RefersTo => loc('Refers to'),
+ ReferredToBy => loc('Referred to by'),
+);
+my %clone;
-my $id = $Ticket->id;
+if ( $Object->isa("RT::Ticket")
+ and $Object->QueueObj->CurrentUserHasRight('CreateTicket'))
+{
+ my $id = $Object->id;
+ my $path
+ = RT->Config->Get('WebPath')
+ . '/Ticket/Create.html?Queue='
+ . $Object->Queue
+ . '&CloneTicket='
+ . $id;
-my $clone = {};
-my $path
- = RT->Config->Get('WebPath')
- . '/Ticket/Create.html?Queue='
- . $Ticket->Queue
- . '&CloneTicket='
- . $id;
-my $can_create = $Ticket->QueueObj->CurrentUserHasRight('CreateTicket');
+ for my $relation (@display) {
+ my $mode = $RT::Link::TYPEMAP{$relation}->{Mode};
+ my $type = $RT::Link::TYPEMAP{$relation}->{Type};
+ my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
+ my @copy = ($id);
-for my $relation (
- qw(RefersTo ReferredToBy)) {
- my $mode = $RT::Ticket::LINKTYPEMAP{$relation}->{Mode};
- my $type = $RT::Ticket::LINKTYPEMAP{$relation}->{Type};
- my $other = "Local" . $mode;
- my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
- $clone->{$field}
- = $path . "&$field="
- . join( '%20',
- ( map { $_->$other() } @{ $Ticket->$relation->ItemsArrayRef } ), $id );
-}
+ # Canonicalized type captures both directions
+ if ($type eq "RefersTo") {
+ my $other = "Local" . $mode;
+ push @copy, map { $_->$other() } @{ $Object->$relation->ItemsArrayRef };
+ }
-for my $relation ( qw(MemberOf Members DependsOn DependedOnBy)) {
- my $mode = $RT::Ticket::LINKTYPEMAP{$relation}->{Mode};
- my $type = $RT::Ticket::LINKTYPEMAP{$relation}->{Type};
- my $field = $mode eq 'Base' ? 'new-' . $type : $type . '-new';
- $clone->{$field} = $path . "&$field=$id";
+ $clone{$relation} = "$path&$field=" . join('%20', grep { $_ } @copy);
+ }
}
-
</%INIT>
<%ARGS>
-$Ticket => undef
+$Object
</%ARGS>
diff --git a/rt/share/html/Elements/ShowLinksOfType b/rt/share/html/Elements/ShowLinksOfType
new file mode 100644
index 0000000..1bb485e
--- /dev/null
+++ b/rt/share/html/Elements/ShowLinksOfType
@@ -0,0 +1,127 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<ul>
+% for my $link (@not_tickets, @active, @inactive) {
+<li><& ShowLink, URI => $link->$ModeURI &>
+<%perl>
+ next unless $Recurse;
+
+ my $ToObj = $link->$ModeObj;
+ next if $ToObj and $checked->{$ToObj->id};
+
+ if ($depth <= $MaxDepth) {
+</%perl>
+<& ShowLinksOfType, %ARGS, Object => $ToObj, depth => ($depth + 1), checked => $checked &>
+% }
+</li>
+% }
+</ul>
+<%INIT>
+return unless $Object;
+
+unless ($RT::Link::TYPEMAP{$Type}) {
+ RT->Logger->error("Unknown link Type '$ARGS{Type}'");
+ return;
+}
+
+unless ($Object->can($Type)) {
+ RT->Logger->error("Don't know how to fetch links of '$Type' for object '$Object'");
+ return;
+}
+
+my $links = $Object->$Type;
+return unless $links->Count;
+
+return if $checked->{$Object->id};
+
+$checked->{$Object->id} = 1;
+
+my $mode = $RT::Link::TYPEMAP{$Type}->{'Mode'};
+my $ModeURI = "${mode}URI";
+my $ModeObj = "${mode}Obj";
+
+# Filter and bucket
+my (@active, @inactive, @not_tickets);
+while (my $link = $links->Next) {
+ my $ToObj = $link->$ModeObj;
+ if ($ToObj and $ToObj->isa('RT::Ticket')) {
+ next if $Type eq "ReferredToBy"
+ and $ToObj->Type eq 'reminder';
+
+ if ( $ToObj->QueueObj->IsInactiveStatus( $ToObj->Status ) ) {
+ push @inactive, $link;
+ }
+ else {
+ push @active, $link;
+ }
+ }
+ else {
+ push @not_tickets, $link;
+ }
+}
+
+$m->callback(
+ CallbackName => "Init",
+ ARGSRef => \%ARGS,
+ Object => $Object,
+ $Type => $Type,
+ $Recurse => \$Recurse,
+ $MaxDepth => \$MaxDepth,
+ active => \@active,
+ inactive => \@inactive,
+ not_tickets => \@not_tickets,
+);
+</%INIT>
+<%ARGS>
+$Object => undef
+$Type
+$Recurse => 0
+$MaxDepth => 7
+$depth => 1
+$checked => {}
+</%ARGS>
diff --git a/rt/share/html/Elements/ShowMemberships b/rt/share/html/Elements/ShowMemberships
index 453beea..7633d68 100644
--- a/rt/share/html/Elements/ShowMemberships
+++ b/rt/share/html/Elements/ShowMemberships
@@ -52,7 +52,7 @@
% if ($Group->Domain eq 'UserDefined') {
<li><a href="<%RT->Config->Get('WebPath')%>/Admin/Groups/Modify.html?id=<% $Group->Id %>"><% $Group->Name %></a></li>
% } elsif ($Group->Domain eq 'SystemInternal') {
-<li><em><% loc($Group->Type) %></em></li>
+<li><em><% loc($Group->Name) %></em></li>
% }
% }
</ul>
@@ -71,12 +71,14 @@ $GroupMembers->Limit(
FIELD => 'Domain',
OPERATOR => '=',
VALUE => 'SystemInternal',
+ CASESENSITIVE => 0,
);
$GroupMembers->Limit(
ALIAS => $alias,
FIELD => 'Domain',
OPERATOR => '=',
VALUE => 'UserDefined',
+ CASESENSITIVE => 0,
);
$GroupMembers->OrderByCols(
{ ALIAS => $alias, FIELD => 'Domain' },
diff --git a/rt/share/html/Elements/ShowMessageHeaders b/rt/share/html/Elements/ShowMessageHeaders
new file mode 100644
index 0000000..27c67bd
--- /dev/null
+++ b/rt/share/html/Elements/ShowMessageHeaders
@@ -0,0 +1,101 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+% if ( @headers ) {
+<table>
+% foreach my $header (@headers) {
+ <tr>
+ <td align="right" class="message-header-key"><% $header->{'Tag'} %>:</td>
+ <td class="message-header-value <% join(' ', map {CSSClass($_)} @{$header->{Classes} || []}) %>">
+ <% $header->{'Value'} | n %></td>
+ </tr>
+% }
+</table>
+% $m->callback( CallbackName => 'AfterHeaders', message => $Message );
+% }
+<%INIT>
+my @headers;
+foreach my $field( RT->Config->Get('ShowBccHeader')? $Message->_SplitHeaders : $Message->SplitHeaders ) {
+ my ($tag, $value) = split /:/, $field, 2;
+ next unless $tag && $value;
+ push @headers, { Tag => $tag, Value => $value };
+}
+
+my %display_headers = map { lc($_) => 1 } @DisplayHeaders;
+
+$m->callback(
+ message => $Message,
+ headers => \@headers,
+ display_headers => \%display_headers,
+);
+
+unless ( $display_headers{'_all'} ) {
+ @headers = grep $display_headers{ lc $_->{'Tag'} }, @headers;
+}
+
+my $object = $Message->TransactionObj->Object;
+foreach my $f (@headers) {
+ $m->comp('/Elements/MakeClicky', content => \$f->{'Value'}, object => $object, %ARGS);
+}
+
+unshift @headers, $m->comp( 'CryptStatus', Message => $Message, WarnUnsigned => $WarnUnsigned );
+
+$m->callback(
+ CallbackName => 'BeforeLocalization',
+ headers => \@headers,
+);
+
+if ( $Localize ) {
+ $_->{'Tag'} = loc($_->{'Tag'}) foreach @headers;
+}
+</%INIT>
+<%ARGS>
+$WarnUnsigned => 0
+$Message => undef
+$Localize => 1
+@DisplayHeaders => ('_all')
+</%ARGS>
diff --git a/rt/share/html/Elements/ShowMessageStanza b/rt/share/html/Elements/ShowMessageStanza
new file mode 100644
index 0000000..f98cb3a
--- /dev/null
+++ b/rt/share/html/Elements/ShowMessageStanza
@@ -0,0 +1,176 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%INIT>
+my $plain_text_mono
+ = RT->Config->Get( 'PlainTextMono', $session{'CurrentUser'} );
+my $Depth = 0;
+
+my $object = $Transaction ? $Transaction->Object : undef;
+
+my $print_content = sub {
+ my $ref = shift;
+ return unless defined $$ref && length $$ref;
+
+ $m->callback( content => $ref, %ARGS );
+ if ( $ContentType eq 'text/plain' ) {
+ $m->comp( '/Elements/MakeClicky',
+ content => $ref,
+ object => $object,
+ %ARGS
+ );
+
+ if ( defined $$ref && !$plain_text_mono ) {
+ $$ref =~ s{(\r?\n)}{<br />}g;
+ }
+ } else {
+ if ( defined $$ref ) {
+ $$ref =~ s/^[\r\n]+//g;
+ }
+ }
+ $m->out($$ref);
+};
+
+$m->out( '<div class="message-stanza'
+ . ( ($ContentType eq 'text/plain' && $plain_text_mono) ? ' plain-text-white-space' : '' ) . '"'
+ . '>' );
+
+if ( ref $Message ) {
+ my @stack;
+ my $para = '';
+ my $i = 0;
+
+AGAIN: foreach ( ; $i < @$Message; $i++ ) {
+ my $stanza = $Message->[$i];
+ if ( ref $stanza eq "HASH" ) {
+ # Fix message stanza nesting for Outlook's quoting styles
+ if ( $stanza->{raw}
+ and not $stanza->{_outlooked}
+ and $stanza->{raw} =~ /^ # start of an internal line
+ \s* # optional whitespace
+ (?:
+ -{3,} # at least three hyphens
+ \s* # whitespace varies between Outlook versions
+ # don't trigger on PGP signed message or signature blocks
+ (?!(?:BEGIN|END)\s+PGP)
+ \w # at least one word character
+ [\w\s]{3,}? # the rest of the word(s), totalling at least 5 characters,
+ # loose to get different languages
+ \w # at least one ending word character
+ \s* # whitespace varies between Outlook versions
+ -{3,} # at least three hyphens again
+ |
+ _{6,} # OR: six or more underscores
+ )
+ \s*$ # optional whitespace until the end of the line
+ /xm )
+ {
+ # There's content before the quoted message, but in the
+ # same stanza. Break it out!
+ if ( my $start = $-[0] ) {
+ my %preceding = %$stanza;
+
+ # We don't process $stanza->{text} because we don't use it
+ # and it isn't given to us by HTML::Quoted. If we ever
+ # need to, we can process it the same way as 'raw'.
+ $preceding{raw} = substr($stanza->{raw}, 0, $start, '');
+
+ # Replace the current stanza with the two we just created
+ splice @$Message, $i, 1, \%preceding, $stanza;
+
+ # Try it again from the top now that we've rejiggered our
+ # stanzas. We'll process the Outlook stanza again, and hit
+ # the else below this time.
+ redo;
+ } else {
+ # Nest the current stanza and everything that follows
+ $stanza->{_outlooked}++;
+ $stanza = $Message->[ $i ] = [ splice @$Message, $i ];
+ }
+ }
+ else {
+ $para .= ( defined $stanza->{raw} ? $stanza->{raw} : '' )."\n";
+ }
+ }
+ next unless ref $stanza eq "ARRAY";
+
+ $print_content->( \$para );
+ $para = '';
+
+ $Depth++;
+ push @stack, [ $Message, $i + 1 ];
+ ( $Message, $i ) = ( $stanza, -1 );
+
+ if ( $Depth == 1 ) {
+ $m->comp('FoldStanzaJS');
+ }
+ my @classes = ('message-stanza');
+ push @classes, $Depth == 1 ? 'closed' : 'open';
+ $m->out( '<div class="' . join(" ", @classes) . '">' );
+ }
+ if ( length $para ) {
+ $print_content->( \$para );
+ $para = '';
+ }
+
+ if (@stack) {
+ ( $Message, $i ) = @{ pop @stack };
+ $Depth--;
+ $m->out('</div>');
+ goto AGAIN;
+ }
+} else {
+ $print_content->( \$Message );
+}
+
+$m->out('</div>');
+</%INIT>
+<%ARGS>
+$Message => undef
+$Transaction => undef
+$ContentType => 'text/plain'
+</%ARGS>
diff --git a/rt/share/html/Elements/ShowUserVerbose b/rt/share/html/Elements/ShowPrincipal
index fd26007..81c8e62 100644
--- a/rt/share/html/Elements/ShowUserVerbose
+++ b/rt/share/html/Elements/ShowPrincipal
@@ -46,27 +46,27 @@
%#
%# END BPS TAGGED BLOCK }}}
%# Released under the terms of version 2 of the GNU Public License
-<% $display |n %>\
-<%INIT>
-my $phrase = '';
-my $address = '';
-my $comment = '';
+<%args>
+$Object
+$PostUser => undef
+$Separator => ", "
+$Link => 1
+</%args>
+<%init>
+if ($Object->isa("RT::Group")) {
+ # Link the users (non-recursively)
+ my @ret = map {$m->scomp("ShowPrincipal", Object => $_->[1], PostUser => $PostUser, Link => $Link)}
+ sort {$a->[0] cmp $b->[0]}
+ map {+[($_->EmailAddress||''), $_]}
+ @{ $Object->UserMembersObj( Recursively => 0 )->ItemsArrayRef };
-if ($User) {
- $address = $User->EmailAddress;
- $phrase = $User->RealName
- if $User->RealName && ( !$address || lc $User->RealName ne lc $address );
- $comment = $User->Name if !$address || lc $User->Name ne lc $address;
+ # But don't link the groups
+ push @ret, sort map {$m->interp->apply_escapes( loc("Group: [_1]", $_->Name), 'h' )}
+ @{ $Object->GroupMembersObj( Recursively => 0)->ItemsArrayRef };
+
+ $m->out( join($Separator, @ret) );
} else {
- $address = $Address;
+ $m->comp("/Elements/ShowUser", User => $Object, Link => $Link);
+ $m->out( $PostUser->($Object) ) if $PostUser;
}
-
-my $display = ($phrase || $comment || '' ) . ($address ? ' <'.$address.'>' : '');
-
-$display = $m->interp->apply_escapes( $display, 'h' )
- unless $ARGS{'NoEscape'};
-</%INIT>
-<%ARGS>
-$User => undef
-$Address => undef
-</%ARGS>
+</%init>
diff --git a/rt/share/html/Elements/ShowRecord b/rt/share/html/Elements/ShowRecord
new file mode 100644
index 0000000..3acff13
--- /dev/null
+++ b/rt/share/html/Elements/ShowRecord
@@ -0,0 +1,100 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%args>
+$Object
+$Format
+$TrustFormat => 0
+$Class => ""
+</%args>
+<%init>
+$Format = ScrubHTML($Format) unless $TrustFormat;
+
+my @columns = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+
+my $fetch_columnmap = sub {
+ my ($name, $attr, $arguments, $no_escape) = @_;
+ my $tmp = $m->comp(
+ '/Elements/ColumnMap',
+ Class => $Object->ColumnMapClassName,
+ Name => $name,
+ Attr => $attr,
+ );
+ return ProcessColumnMapValue( $tmp, Arguments => $arguments, Escape => !$no_escape );
+};
+</%init>
+<div class="record <% CSSClass($Class) %> <% CSSClass(blessed($Object)) %>">
+<%perl>
+for my $column (@columns) {
+ my $title = $m->interp->apply_escapes($column->{title} || '', 'h');
+ my $attr = $column->{'attribute'} || $column->{'last_attribute'};
+
+ unless (defined $column->{title}) {
+ # No format-supplied title, so use the one from the column map as-is. It's
+ # trustworthy.
+ $title = $fetch_columnmap->($attr,'title',[$attr]);
+ }
+</%perl>
+<div class="record-field <% $Class ? CSSClass("$Class-field") : "" %> <% CSSClass($fetch_columnmap->($attr,'attribute',[$attr],'no_escape')) %>">
+<span class="label"><% loc($title) |n %></span>
+<%perl>
+ my @out;
+ foreach my $subcol ( @{ $column->{output} } ) {
+ my ($col) = ($subcol =~ /^__(.*?)__$/);
+ unless ( $col ) {
+ push @out, $subcol;
+ next;
+ }
+ push @out, $fetch_columnmap->($col, 'value', [$Object]);
+ }
+ @out = grep { defined $_ and length $_ } @out;
+</%perl>
+<span class="value"><% join('',@out) |n %></span>
+% $m->callback(CallbackName => 'AfterValue', Object => $Object, attribute => $attr, column => $column );
+</div>
+% }
+</div>
diff --git a/rt/share/html/Elements/ShowRelationLabel b/rt/share/html/Elements/ShowRelationLabel
index 92d8fec..e1c684f 100644
--- a/rt/share/html/Elements/ShowRelationLabel
+++ b/rt/share/html/Elements/ShowRelationLabel
@@ -45,18 +45,36 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<a href="<%$SearchURL |n %>"><%$Label%></a>
+% if ($SearchURL) {
+<a href="<% $SearchURL %>"><% $Label %></a>
+% } else {
+<% $Label %>
+% }
<%INIT>
-my $typemap = RT::Ticket->LINKTYPEMAP->{$Relation};
+my $typemap = $RT::Link::TYPEMAP{$Relation};
my $search_mode = $typemap->{Mode};
my $search_type = $typemap->{Type};
-my $search_relation = RT::Ticket->LINKDIRMAP->{$search_type}{$search_mode};
+my $search_relation = $RT::Link::DIRMAP{$search_type}->{$search_mode};
+my $SearchURL;
-my $Query = $search_relation . ' = ' . $id;
-my $SearchURL = RT->Config->Get('WebPath') . '/Search/Results.html?' . $m->comp('/Elements/QueryString', Query => $Query);
+if ($Object and $Object->id) {
+ my $id = $Object->id;
+
+ if ($Object->isa("RT::Ticket")) {
+ $SearchURL = RT->Config->Get('WebPath')
+ . '/Search/Results.html?'
+ . $m->comp('/Elements/QueryString', Query => "$search_relation = $id");
+ }
+}
+
+$m->callback(
+ CallbackName => "ModifySearchURL",
+ SearchURL => \$SearchURL,
+ ARGSRef => \%ARGS,
+);
</%INIT>
<%ARGS>
-$id
+$Object => undef
$Label
$Relation
</%ARGS>
diff --git a/rt/share/html/Elements/ShowReminders b/rt/share/html/Elements/ShowReminders
index 6b5ad39..61d8040 100644
--- a/rt/share/html/Elements/ShowReminders
+++ b/rt/share/html/Elements/ShowReminders
@@ -57,7 +57,7 @@ my $i =0;
while ( my $reminder = $reminders->Next ) {
$i++;
my $dueobj = $reminder->DueObj;
-my $overdue = $dueobj->Unix > 0 && $dueobj->Diff < 0 ? 1 : 0;
+my $overdue = $dueobj->IsSet && $dueobj->Diff < 0 ? 1 : 0;
my $targets = RT::Tickets->new($session{'CurrentUser'});
$targets->{'allow_deleted_search'} = 1;
@@ -67,7 +67,7 @@ if ( my $ticket= $targets->First ) {
</%PERL>
<tr class="<% $i%2 ? 'oddline' : 'evenline' %>">
<td class="collection-as-table">
-<a href="<% RT->Config->Get('WebPath') %>/Ticket/Reminders.html?id=<% $ticket->id %>"><% $reminder->Subject %></a>
+<a href="<% RT->Config->Get('WebPath') %>/Ticket/Reminders.html?id=<% $ticket->id %>#reminder-<% $reminder->id %>"><% $reminder->Subject %></a>
</td>
<td class="collection-as-table">
<% $overdue ? '<span class="overdue">' : '' |n %><% $dueobj->AgeAsString || loc('Not set') %><% $overdue ? '</span>' : '' |n %>
@@ -76,7 +76,7 @@ if ( my $ticket= $targets->First ) {
<a href="<% RT->Config->Get( 'WebPath' ) %>/Ticket/Display.html?id=<% $ticket->id %>">#<% $ticket->Id %>: <% $ticket->Subject %></a>
</td>
% } else {
-<td colspan="3" class="collection-as-table>
+<td colspan="3" class="collection-as-table">
<div class="error"><div class="error">Couldn't find Ticket for reminder <% $reminder->id %>. Please contact administrator.</div></div>
</td>
% }
@@ -91,7 +91,7 @@ my $tsql = 'Type = "reminder"' .
' AND ( Owner = "Nobody" OR Owner ="' . $session{'CurrentUser'}->id . '")' .
' AND ( Status = "new" OR Status = "open" )';
-$tsql .= ' AND Due < "now"' if $OnlyOverdue;
+$tsql .= ' AND ( Due < "now" OR Due IS NULL )' if $OnlyOverdue;
$reminders->FromSQL($tsql);
$reminders->OrderBy( FIELD => 'Due', ORDER => 'ASC' );
diff --git a/rt/share/html/Elements/ShowSearch b/rt/share/html/Elements/ShowSearch
index 20ce55b..b53238f 100644
--- a/rt/share/html/Elements/ShowSearch
+++ b/rt/share/html/Elements/ShowSearch
@@ -67,9 +67,10 @@ if ($SavedSearch) {
$m->out(loc("Either you have no rights to view saved search [_1] or identifier is incorrect", $m->interp->apply_escapes($SavedSearch, 'h')));
return;
}
- $search = $container_object->Attributes->WithId($search_id);
+ $search = RT::Attribute->new( $session{'CurrentUser'} );
+ $search->Load($search_id);
unless ( $search->Id && ref( $SearchArg = $search->Content ) eq 'HASH' ) {
- $m->out(loc("Saved Search [_1] not found", $m->interp->apply_escapes($SavedSearch, 'h'))) unless $IgnoreMissing;
+ $m->out(loc("Saved search [_1] not found", $m->interp->apply_escapes($SavedSearch, 'h'))) unless $IgnoreMissing;
return;
}
$SearchArg->{'SavedSearchId'} ||= $SavedSearch;
@@ -122,17 +123,21 @@ foreach ( $SearchArg, $ProcessedSearchArg ) {
$_->{'Format'} ||= '';
$_->{'Query'} ||= '';
- $_->{'Format'} =~ s/__(Web(?:Path|Base|BaseURL))__/scalar RT->Config->Get($1)/ge;
# extract-message-catalog would "$1", so we avoid quotes for loc calls
$_->{'Format'} =~ s/__loc\(["']?(\w+)["']?\)__/my $f = "$1"; loc($f)/ge;
if ( $_->{'Query'} =~ /__Bookmarks__/ ) {
$_->{'Rows'} = 999;
- # DEPRECATED: will be here for a while up to 3.10/4.0
- my $bookmarks = $session{'CurrentUser'}->UserObj->FirstAttribute('Bookmarks');
- $bookmarks = $bookmarks->Content if $bookmarks;
- $bookmarks ||= {};
- my $query = join(" OR ", map " id = '$_' ", grep $bookmarks->{ $_ }, keys %$bookmarks ) || 'id=0';
+ # DEPRECATED: will be here for a while up to 4.4
+ RT->Deprecated(
+ Remove => "4.4",
+ Instead => "id = '__Bookmarked__'",
+ Message => "The __Bookmarks__ query syntax is deprecated",
+ Object => $search,
+ );
+
+ my @bookmarks = $session{'CurrentUser'}->UserObj->Bookmarks;
+ my $query = join(" OR ", map " id = '$_' ", @bookmarks ) || 'id=0';
$_->{'Query'} =~ s/__Bookmarks__/( $query )/g;
}
}
diff --git a/rt/share/html/Elements/ShowTransaction b/rt/share/html/Elements/ShowTransaction
new file mode 100644
index 0000000..1018ec6
--- /dev/null
+++ b/rt/share/html/Elements/ShowTransaction
@@ -0,0 +1,265 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<div class="<% join ' ', @classes %>">
+ <div class="metadata">
+ <span class="type">
+ <a name="txn-<% $Transaction->id %>" \
+% if ( $DisplayPath ) {
+ href="<% $DisplayPath %>?id=<% $Object->id %>#txn-<% $Transaction->id %>" \
+% }
+ >#</a>
+ </span>
+% $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterAnchor' );
+ <span class="date"><% $date |n %></span>
+ <span class="description">
+ <& /Elements/ShowUser, User => $Transaction->CreatorObj &> - <% $desc |n %>
+% $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterDescription' );
+ </span>
+ <span class="time-taken"><% $time %></span>
+% if ( $actions ) {
+ <span class="actions"><% $actions |n %></span>
+% }
+ </div>
+
+ <div class="content">
+<%PERL>
+$m->comp('/Elements/ShowCustomFields', Object => $Transaction, HideEmpty => 1 ) if $HasTxnCFs;
+$m->comp(
+ 'ShowTransactionAttachments',
+ %ARGS,
+ Parent => 0
+) if $ShowBody;
+</%PERL>
+ </div>
+% $m->callback( %ARGS, Transaction => $Transaction, CallbackName => 'AfterContent' );
+</div>
+
+<%ARGS>
+$Transaction
+$Object => $Transaction->Object
+
+$Attachments => undef
+$AttachmentContent => undef
+$HasTxnCFs => 1
+
+$ShowBody => 1
+$ShowActions => 1
+$RowNum => 1
+
+$DisplayPath => undef
+$AttachmentPath => undef
+$UpdatePath => undef
+$ForwardPath => undef
+$EncryptionPath => undef
+$EmailRecordPath => undef
+</%ARGS>
+
+<%ONCE>
+
+</%ONCE>
+<%INIT>
+my $record_type = $Object->RecordType;
+my $type_class = $Object->ClassifyTransaction( $Transaction );
+
+$m->callback(
+ CallbackName => 'MassageTypeClass',
+ Transaction => $Transaction,
+ TypeClassRef => \$type_class,
+ ARGSRef => \%ARGS,
+);
+
+my @classes = (
+ "transaction",
+ "$record_type-transaction",
+ $type_class,
+ ($RowNum % 2 ? 'odd' : 'even')
+);
+
+my $desc = $Transaction->BriefDescriptionAsHTML;
+if ( $Object->id != $Transaction->ObjectId ) {
+ # merged objects
+ $desc = join " - ",
+ $m->interp->apply_escapes(
+ loc("[_1] #[_2]:", loc($record_type), $Transaction->ObjectId), 'h'),
+ $desc;
+}
+
+my $date = $Transaction->CreatedAsString;
+
+my $time = '';
+$time = loc('[quant,_1,minute,minutes]', $Transaction->TimeTaken)
+ if $Transaction->TimeTaken;
+
+if ( $ShowBody && !$Attachments ) {
+ $ARGS{'Attachments'} = $Attachments = {};
+
+ my $attachments = $Transaction->Attachments( WithHeaders => 1 );
+ push @{ $Attachments->{ $_->Parent || 0 } ||= [] }, $_
+ foreach @{ $attachments->ItemsArrayRef };
+}
+
+my @actions = ();
+my $txn_type = $Transaction->Type;
+if ( $txn_type =~ /EmailRecord$/ ) {
+ push @actions, {
+ title => loc('Show'),
+ target => '_blank',
+ path => $EmailRecordPath
+ .'?id='. $Object->id
+ .'&Transaction='. $Transaction->id
+ .'&Attachment='. ( $Attachments->{0}[0] && $Attachments->{0}[0]->id ),
+ } if $EmailRecordPath;
+
+ $ShowBody = 0;
+}
+
+# If the transaction has anything attached to it at all
+elsif ( %$Attachments && $ShowActions ) {
+ my %has_right = map {
+ $_ => RT::ACE->CanonicalizeRightName( $_ . $record_type )
+ } qw(Modify CommentOn ReplyTo);
+ $has_right{'Forward'} = RT::ACE->CanonicalizeRightName('ForwardMessage');
+
+ my $can_modify = $has_right{'Modify'}
+ && $Object->CurrentUserHasRight( $has_right{'Modify'} );
+
+ if ( $UpdatePath && $has_right{'ReplyTo'}
+ && ( $can_modify
+ || $Object->CurrentUserHasRight( $has_right{'ReplyTo'} )
+ )
+ ) {
+ push @actions, {
+ class => "reply-link",
+ title => loc('Reply'),
+ path => $UpdatePath
+ .'?id='. $Object->id
+ .'&QuoteTransaction='. $Transaction->id
+ .'&Action=Respond'
+ ,
+ };
+ }
+ if ( $UpdatePath && $has_right{'CommentOn'}
+ && ( $can_modify
+ || $Object->CurrentUserHasRight( $has_right{'CommentOn'} )
+ )
+ ) {
+ push @actions, {
+ class => "comment-link",
+ title => loc('Comment'),
+ path => $UpdatePath
+ .'?id='. $Object->id
+ .'&QuoteTransaction='. $Transaction->id
+ .'&Action=Comment'
+ ,
+ };
+ }
+ if ( $ForwardPath && $has_right{'Forward'}
+ && $Object->CurrentUserHasRight( $has_right{'Forward'} )
+ ) {
+ push @actions, {
+ class => "forward-link",
+ title => loc('Forward'),
+ path => $ForwardPath
+ .'?id='. $Object->id
+ .'&QuoteTransaction='. $Transaction->id
+ ,
+ };
+ }
+ if ( $EncryptionPath && $can_modify
+ && RT->Config->Get('Crypt')->{'Enable'}
+ && RT->Config->Get('Crypt')->{'AllowEncryptDataInDB'}
+ ) {
+ push @actions, {
+ class => "encryption-link",
+ title => loc('Encrypt/Decrypt'),
+ path => $EncryptionPath
+ .'?id='. $Transaction->id
+ .'&QuoteTransaction='. $Transaction->id
+ ,
+ };
+ }
+}
+
+$m->callback(
+ %ARGS,
+ Transaction => $Transaction,
+ Object => $Object,
+
+ Classes => \@classes,
+ Actions => \@actions,
+ Created => \$date,
+ TimeTaken => \$time,
+ Description => \$desc,
+ ShowBody => \$ShowBody,
+);
+
+my $actions = '';
+if ( @actions ) {
+ my $i = $m->interp;
+
+ foreach my $a ( @actions ) {
+ $a = '<a'
+ .' href="'. $i->apply_escapes( $a->{'path'}, 'h' ) .'"'
+ . ($a->{'target'}
+ ? ' target="'. $i->apply_escapes( $a->{'target'}, 'h' ) .'"'
+ : ''
+ )
+ . ($a->{'class'}
+ ? ' class="'. $i->apply_escapes( $a->{'class'}, 'h' ) .'"'
+ : ''
+ )
+ .'>'. $i->apply_escapes( $a->{'title'}, 'h' ) .'</a>'
+ ;
+ }
+ $actions = join '&nbsp;', map "[$_]", @actions;
+}
+
+# make date unbreakable
+$date = $m->interp->apply_escapes( $date, 'h' );
+$date =~ s/\s/&nbsp;/g;
+</%INIT>
diff --git a/rt/share/html/Elements/ShowTransactionAttachments b/rt/share/html/Elements/ShowTransactionAttachments
new file mode 100644
index 0000000..7aeded6
--- /dev/null
+++ b/rt/share/html/Elements/ShowTransactionAttachments
@@ -0,0 +1,293 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%PERL>
+# Find all the attachments which have parent $Parent
+# For each of these attachments
+foreach my $message ( @{ $Attachments->{ $Parent || 0 } || [] } ) {
+ $m->comp( 'ShowMessageHeaders',
+ WarnUnsigned => $WarnUnsigned,
+ Message => $message,
+ DisplayHeaders => \@DisplayHeaders,
+ );
+
+ my $name = defined $message->Filename && length $message->Filename ? $message->Filename : '';
+ if ( $message->ContentLength or $name ) {
+</%PERL>
+<div class="downloadattachment">
+<a href="<% $AttachmentPath %>/<% $Transaction->Id %>/<% $message->Id %>/<% $name | u%>"><&|/l&>Download</&> <% length $name ? $name : loc('(untitled)') %></a>\
+% if ( $DownloadableHeaders && ! length $name && $message->ContentType =~ /text/ ) {
+ / <a href="<% $AttachmentPath %>/WithHeaders/<% $message->Id %>"><% loc('with headers') %></a>
+% }
+% $m->callback(CallbackName => 'AfterDownloadLinks', ARGSRef => \%ARGS, Object => $Object, Transaction => $Transaction, Attachment => $message);
+<br />
+<span class="downloadcontenttype"><% $message->ContentType %> <% $message->FriendlyContentLength %></span>
+</div>
+% }
+%# If there is sub-messages, open a dedicated div
+% if ( $Attachments->{ $message->id } ) {
+<div class="messageattachments">
+% } else {
+<div class="messagebody">
+% }
+<%PERL>
+
+$render_attachment->( $message );
+
+$m->comp(
+ $m->current_comp,
+ %ARGS,
+ Parent => $message->id,
+ ParentObj => $message,
+
+ displayed_inline => $displayed_inline,
+);
+
+</%PERL>
+</div>
+% }
+<%ARGS>
+$Transaction
+$Object => $Transaction->Object
+$ShowHeaders => 0
+$DownloadableHeaders => 1
+$AttachmentPath => undef
+$Attachments => {}
+$AttachmentContent => {}
+$Parent => 0
+$ParentObj => undef
+$WarnUnsigned => 0
+
+# Keep track of CID images we display inline
+$displayed_inline => {}
+</%ARGS>
+<%INIT>
+my @DisplayHeaders=qw(_all);
+if ( $Transaction->Type =~ /EmailRecord$/ ) {
+ @DisplayHeaders = qw(To Cc Bcc);
+}
+
+# If the transaction has anything attached to it at all
+elsif (!$ShowHeaders) {
+ @DisplayHeaders = qw(To From RT-Send-Cc Cc Bcc Date Subject);
+ push @DisplayHeaders, 'RT-Send-Bcc' if RT->Config->Get('ShowBccHeader');
+}
+
+$m->callback(CallbackName => 'MassageDisplayHeaders', DisplayHeaders => \@DisplayHeaders, Transaction => $Transaction, ShowHeaders => $ShowHeaders);
+
+my $render_attachment = sub {
+ my $message = shift;
+ my $name = defined $message->Filename && length $message->Filename ? $message->Filename : '';
+
+ my $content_type = lc $message->ContentType;
+
+ # if it has a content-disposition: attachment, don't show inline
+ my $disposition = $message->GetHeader('Content-Disposition');
+
+ if ( $disposition && $disposition =~ /^\s*attachment/i ) {
+ $disposition = 'attachment';
+ } else {
+ $disposition = 'inline';
+ }
+
+ # If it's text
+ if ( $content_type =~ m{^(text|message)/} ) {
+ my $max_size = RT->Config->Get( 'MaxInlineBody', $session{'CurrentUser'} );
+ if ( $disposition ne 'inline' ) {
+ $m->out('<p>'. loc( 'Message body is not shown because sender requested not to inline it.' ) .'</p>');
+ return;
+ }
+ elsif ( length $name && RT->Config->Get('SuppressInlineTextFiles', $session{'CurrentUser'} ) ) {
+ $m->out('<p>'. loc( 'Text file is not shown because it is disabled in preferences.' ) .'</p>');
+ return;
+ }
+ elsif ( $max_size && $message->ContentLength > $max_size ) {
+ $m->out('<p>'. loc( 'Message body is not shown because it is too large.' ) .'</p>');
+ return;
+ }
+
+ if (
+
+ # it's a toplevel object
+ !$ParentObj
+
+ # or its parent isn't a multipart alternative
+ || ( $ParentObj->ContentType !~ m{^multipart/(?:alternative|related)$}i )
+
+ # or it's of our prefered alterative type
+ || (
+ (
+ RT->Config->Get('PreferRichText', $session{CurrentUser})
+ && ( $content_type =~ m{^text/(?:html|enriched)$} )
+ )
+ || ( !RT->Config->Get('PreferRichText', $session{CurrentUser})
+ && ( $content_type !~ m{^text/(?:html|enriched)$} )
+ )
+ )
+ ) {
+
+ my $content;
+ # If we've cached the content, use it from there
+ if (my $x = $AttachmentContent->{ $Transaction->id }->{$message->id}) {
+ $content = $x->Content;
+ }
+ else {
+ $content = $message->Content;
+ }
+
+ $RT::Logger->debug(
+ "Rendering attachment #". $message->id
+ ." of '$content_type' type"
+ );
+
+ # if it's a text/html clean the body and show it
+ if ( $content_type eq 'text/html' ) {
+ $content = $m->comp( '/Elements/ScrubHTML', Content => $content );
+
+ if (RT->Config->Get('ShowTransactionImages')) {
+ my @rewritten = RT::Interface::Web::RewriteInlineImages(
+ Content => \$content,
+ Attachment => $message,
+ # Not technically correct to search all parts of the
+ # MIME structure, but it saves having to go to the
+ # database again and is unlikely to break display.
+ Related => [ map { @$_ } values %$Attachments ],
+ AttachmentPath => $AttachmentPath,
+ );
+ $displayed_inline->{$_}++ for @rewritten;
+ }
+
+ $m->comp(
+ '/Elements/MakeClicky',
+ content => \$content,
+ html => 1,
+ object => $Object,
+ );
+
+ if ( !length $name && RT->Config->Get( 'QuoteFolding', $session{CurrentUser} ) ) {
+ eval {
+ require HTML::Quoted;
+ $content = HTML::Quoted->extract($content)
+ };
+ if ($@) {
+ RT->Logger->error(
+ "HTML::Quoted couldn't process attachment #@{[$message->id]}: $@."
+ . " This is a bug, please report it to rt-bugs\@bestpractical.com.");
+ }
+ }
+
+ $m->comp(
+ 'ShowMessageStanza',
+ Message => $content,
+ Transaction => $Transaction,
+ ContentType => 'text/html',
+ );
+ }
+
+ elsif ( $content_type eq 'text/enriched' ) {
+ $content = $m->comp( '/Elements/ScrubHTML', Content => $content );
+ $m->out( $content );
+ }
+
+ # It's a text type we don't have special handling for
+ else {
+ if ( !length $name && RT->Config->Get( 'QuoteFolding', $session{CurrentUser} ) ) {
+ eval {
+ require Text::Quoted;
+ Text::Quoted::set_quote_characters(undef);
+ $content = Text::Quoted::extract($content);
+ };
+ if ($@) {
+ RT->Logger->error(
+ "Text::Quoted couldn't process attachment #@{[$message->id]}: $@."
+ . " This is a bug, please report it to rt-bugs\@bestpractical.com.");
+ }
+ }
+
+ $m->comp(
+ 'ShowMessageStanza',
+ Message => $content,
+ Transaction => $Transaction,
+ ContentType => 'text/plain',
+ );
+ }
+ }
+ }
+
+ # if it's an image, show it as an image
+ elsif ( $content_type =~ m{^image/} ) {
+ if (not RT->Config->Get('ShowTransactionImages')) {
+ $m->out('<p><i>'. loc( 'Image not shown because display is disabled in system configuration.' ) .'</i></p>');
+ return;
+ }
+ elsif ( $displayed_inline->{$message->Id} ) {
+ $m->out('<p><i>'. loc( 'Image displayed inline above' ) .'</i></p>');
+ return;
+ }
+ elsif ( $disposition ne 'inline' ) {
+ $m->out('<p>'. loc( 'Image not shown because sender requested not to inline it.' ) .'</p>');
+ return;
+ }
+
+ my $filename = length $name ? $name : loc('(untitled)');
+ my $efilename = $m->interp->apply_escapes( $filename, 'h' );
+ $m->out(
+ qq{<img alt="$efilename" title="$efilename"}
+ . ' src="'. $AttachmentPath .'/'. $Transaction->Id .'/'. $message->Id .'/'
+ . $m->interp->apply_escapes( $filename, 'u', 'h' )
+ . '" />'
+ );
+ }
+ elsif ( $message->ContentLength && $message->ContentLength > 0 ) {
+ $m->out( '<p>' .
+ loc( 'Message body not shown because it is not plain text.' ) .
+ '</p>'
+ );
+ }
+};
+
+</%INIT>
diff --git a/rt/share/html/Elements/ShowUser b/rt/share/html/Elements/ShowUser
index c58d33b..dd487a8 100644
--- a/rt/share/html/Elements/ShowUser
+++ b/rt/share/html/Elements/ShowUser
@@ -45,28 +45,51 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-%# Released under the terms of version 2 of the GNU Public License
<%INIT>
# $User is an RT::User object
# $Address is Email::Address object
-my $comp = '/Elements/ShowUser'. ucfirst lc $style;
-unless ( RT::Interface::Web->ComponentPathIsSafe($comp) and $m->comp_exists( $comp ) ) {
- $RT::Logger->error(
- 'Either system config or user #'
- . $session{'CurrentUser'}->id
- . ' picked UsernameFormat '. $style
- . ', but '. $comp . "doesn't exist"
- );
- return $m->comp('/Elements/ShowUserConcise',
- User => $User, Address => $Address, NoEscape => $NoEscape
- );
+my $display = RT::User->Format(
+ User => $User,
+ Address => $Address,
+ CurrentUser => $session{CurrentUser},
+ Format => $style,
+);
+
+# RT::User->Format does this itself, but we want to make sure we have a $User
+# if at all possible for the rest of our code below.
+if ($Address and not $User) {
+ $User = RT::User->new( $session{CurrentUser} );
+ $User->LoadByEmail( $Address->address );
+ undef $User unless $User->id;
}
-return $m->comp( $comp, User => $User, Address => $Address, NoEscape => $NoEscape );
+
+my %system_user = (
+ RT->Nobody->id => 1,
+ RT->SystemUser->id => 1,
+);
+
+$m->callback(
+ ARGSRef => \%ARGS,
+ User => $User,
+ Address => $Address,
+ display => \$display,
+ system_user => \%system_user,
+ CallbackName => 'Modify',
+);
</%INIT>
<%ARGS>
$User => undef
$Address => undef
-$NoEscape => 0
-$style => RT->Config->Get('UsernameFormat', $session{'CurrentUser'})
+$style => undef
+$Link => 1
</%ARGS>
+<span class="user" <% $User && $User->id ? 'data-user-id="'.$User->id.'"' : "" |n %>>\
+% if ($Link and $User and $User->id and not $system_user{$User->id} and $session{CurrentUser}->Privileged) {
+<a href="<% RT->Config->Get("WebPath") %>/User/Summary.html?id=<% $User->id %>">\
+<% $display %>\
+</a>\
+% } else {
+<% $display %>\
+% }
+</span>\
diff --git a/rt/share/html/Elements/SimpleSearch b/rt/share/html/Elements/SimpleSearch
index d6287f1..7db2aca 100755
--- a/rt/share/html/Elements/SimpleSearch
+++ b/rt/share/html/Elements/SimpleSearch
@@ -46,8 +46,11 @@
%#
%# END BPS TAGGED BLOCK }}}
<form action="<% RT->Config->Get('WebPath') %><% $SendTo %>" id="simple-search">
- <input size="12" name="q" autocomplete="off" accesskey="0" class="field" value="<% $Placeholder %>..." onfocus="if (this.value==(<% $Placeholder, |n,j %>+'...')) this.value=''" />
+ <input size="12" name="q" accesskey="0" class="field" value="<% $value %>" placeholder="<% $Placeholder %>..." />
</form>
+<%init>
+my $value = defined $DECODED_ARGS->{q} ? $DECODED_ARGS->{q} : '';
+</%init>
<%ARGS>
$SendTo => '/Search/Simple.html'
$Placeholder => loc('Search')
diff --git a/rt/share/html/Elements/Submit b/rt/share/html/Elements/Submit
index c26c468..a5dcdf7 100755
--- a/rt/share/html/Elements/Submit
+++ b/rt/share/html/Elements/Submit
@@ -52,10 +52,10 @@ id="<%$id%>"
>
<div class="extra-buttons">
% if ($CheckAll) {
- <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this.form, <% $match %>, true);return false;" class="button" />
+ <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this, <% $match %>, true);return false;" class="button" />
% }
% if ($ClearAll) {
- <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this.form, <% $match %>, false);return false;" class="button" />
+ <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this, <% $match %>, false);return false;" class="button" />
% }
% if ($Reset) {
<input type="reset" value="<%$ResetLabel%>" class="button" />
diff --git a/rt/share/html/Elements/TSVExport b/rt/share/html/Elements/TSVExport
new file mode 100644
index 0000000..0c466c4
--- /dev/null
+++ b/rt/share/html/Elements/TSVExport
@@ -0,0 +1,131 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+%# <sales@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Class => undef
+$Collection
+$Format
+$PreserveNewLines => 0
+</%ARGS>
+<%ONCE>
+my $no_html = HTML::Scrubber->new( deny => '*' );
+</%ONCE>
+<%INIT>
+require HTML::Entities;
+$Class ||= $Collection->ColumnMapClassName;
+
+#no, it isn't# $r->content_type('application/vnd.ms-excel');
+$r->content_type('text/tab-separated-values');
+
+my $DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $Format);
+
+my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat);
+
+my @columns;
+
+my $should_loc = { map { $_ => 1 } qw(Status) };
+
+my $col_entry = sub {
+ my $col = shift;
+ # in tsv output, "#" is often a comment character but we use it for "id"
+ delete $col->{title}
+ if $col->{title} and $col->{title} =~ /^\s*#\s*$/;
+ return {
+ header => loc($col->{title} || $col->{attribute}),
+ map => $m->comp(
+ "/Elements/ColumnMap",
+ Name => $col->{attribute},
+ Attr => 'value',
+ Class => $Class,
+ ),
+ should_loc => $should_loc->{$col->{attribute}},
+ }
+};
+
+if ($PreserveNewLines) {
+ my $col = [];
+ push @columns, $col;
+ for (@Format) {
+ if ($_->{title} eq 'NEWLINE') {
+ $col = [];
+ push @columns, $col;
+ }
+ else {
+ push @$col, $col_entry->($_);
+ }
+ }
+}
+else {
+ push @columns, [map { $_->{attribute}
+ ? $col_entry->($_)
+ : () } @Format];
+}
+
+for (@columns) {
+ $m->out(join("\t", map { $_->{header} } @$_)."\n");
+}
+
+my $i = 0;
+my $ii = 0;
+while (my $row = $Collection->Next) {
+ for my $col (@columns) {
+ $m->out(join("\t", map {
+ my $val = ProcessColumnMapValue($_->{map}, Arguments => [$row, $ii++], Escape => 0);
+ $val = loc($val) if $_->{should_loc};
+ # remove tabs from all field values, they screw up the tsv
+ $val = '' unless defined $val;
+ $val =~ s/(?:\n|\r)+/ /g; $val =~ s{\t}{ }g;
+ $val = $no_html->scrub($val);
+ $val = HTML::Entities::decode_entities($val);
+ $val;
+ } @$col)."\n");
+ }
+ $m->flush_buffer unless ++$i % 10;
+}
+$m->abort();
+
+</%INIT>
diff --git a/rt/share/html/Elements/Tabs b/rt/share/html/Elements/Tabs
index 3e28e25..46d2bd8 100755
--- a/rt/share/html/Elements/Tabs
+++ b/rt/share/html/Elements/Tabs
@@ -62,7 +62,7 @@ my $query_string = sub {
my $build_admin_menu = sub {
my $top = shift;
- my $admin = $top->child( config => title => loc('Configuration'), path => '/Admin/', sort_order => 99 );
+ my $admin = $top->child( admin => title => loc('Admin'), path => '/Admin/' );
if ( $session{'CurrentUser'}->HasRight( Object => RT->System, Right => 'AdminUsers' ) ) {
my $users = $admin->child( users =>
title => loc('Users'),
@@ -98,6 +98,16 @@ my $build_admin_menu = sub {
$cfs->child( create => title => loc('Create'), path => "/Admin/CustomFields/Modify.html?Create=1" );
}
+ if ( $session{'CurrentUser'}->HasRight( Object => RT->System, Right => 'ModifyScrips' ) ) {
+ my $scrips = $admin->child( 'scrips' =>
+ title => loc('Scrips'),
+ description => loc('Manage scrips'),
+ path => '/Admin/Scrips/',
+ );
+ $scrips->child( select => title => loc('Select'), path => "/Admin/Scrips/" );
+ $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html" );
+ }
+
my $admin_global = $admin->child( global =>
title => loc('Global'),
description => loc('Manage properties and configuration which apply to all queues'),
@@ -110,7 +120,7 @@ my $build_admin_menu = sub {
path => '/Admin/Global/Scrips.html',
);
$scrips->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" );
- $scrips->child( create => title => loc('Create'), path => "/Admin/Global/Scrip.html?Create=1" );
+ $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Global=1" );
my $templates = $admin_global->child( templates =>
title => loc('Templates'),
@@ -172,11 +182,11 @@ my $build_admin_menu = sub {
my $cfs = $article_admin->child( 'custom-fields' =>
title => loc('Custom Fields'),
- path => '/Admin/CustomFields/index.html?'.$m->comp('/Elements/QueryString', type => 'RT::Class-RT::Article'),
+ path => '/Admin/CustomFields/index.html?'.$m->comp('/Elements/QueryString', Type => 'RT::Class-RT::Article'),
);
$cfs->child( select =>
title => loc('Select'),
- path => '/Admin/CustomFields/index.html?'.$m->comp('/Elements/QueryString', type => 'RT::Class-RT::Article'),
+ path => '/Admin/CustomFields/index.html?'.$m->comp('/Elements/QueryString', Type => 'RT::Class-RT::Article'),
);
$cfs->child( create =>
title => loc('Create'),
@@ -198,6 +208,11 @@ my $build_admin_menu = sub {
description => loc('Modify the default "RT at a glance" view'),
path => '/Admin/Global/MyRT.html',
);
+ $admin_global->child( 'dashboards-in-menu' =>
+ title => loc('Dashboards in menu'),
+ description => loc('Customize dashboards in menu'),
+ path => '/Admin/Global/DashboardsInMenu.html',
+ );
$admin_global->child( 'topics' =>
title => loc('Topics'),
description => loc('Modify global article topics'),
@@ -281,12 +296,13 @@ my $build_admin_menu = sub {
my $scrips = $queue->child( scrips => title => loc('Scrips'), path => "/Admin/Queues/Scrips.html?id=" . $id);
$scrips->child( select => title => loc('Select'), path => "/Admin/Queues/Scrips.html?id=" . $id );
- $scrips->child( create => title => loc('Create'), path => "/Admin/Queues/Scrip.html?Create=1;Queue=" . $id);
+ $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Queue=" . $id);
- my $ticket_cfs = $queue->child( 'ticket-custom-fields' => title => loc('Ticket Custom Fields'),
+ my $cfs = $queue->child( 'custom-fields' => title => loc('Custom Fields') );
+ my $ticket_cfs = $cfs->child( 'tickets' => title => loc('Tickets'),
path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket&id=' . $id );
- my $txn_cfs = $queue->child( 'transaction-custom-fields' => title => loc('Transaction Custom Fields'),
+ my $txn_cfs = $cfs->child( 'transactions' => title => loc('Transactions'),
path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket-RT::Transaction&id='.$id );
$queue->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Queues/GroupRights.html?id=".$id );
@@ -297,7 +313,7 @@ my $build_admin_menu = sub {
}
}
}
- if ( $request_path =~ m{^/Admin/Users} ) {
+ if ( $request_path =~ m{^(/Admin/Users|/User/(Summary|History)\.html)} and $admin->child("users") ) {
if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
my $id = $DECODED_ARGS->{'id'};
my $obj = RT::User->new( $session{'CurrentUser'} );
@@ -309,9 +325,14 @@ my $build_admin_menu = sub {
$tabs->child( memberships => title => loc('Memberships'), path => "/Admin/Users/Memberships.html?id=" . $id );
$tabs->child( history => title => loc('History'), path => "/Admin/Users/History.html?id=" . $id );
$tabs->child( 'my-rt' => title => loc('RT at a glance'), path => "/Admin/Users/MyRT.html?id=" . $id );
- if ( RT->Config->Get('GnuPG')->{'Enable'} ) {
- $tabs->child( pgp => title => loc('GnuPG'), path => "/Admin/Users/GnuPG.html?id=" . $id );
+ $tabs->child( 'dashboards-in-menu' =>
+ title => loc('Dashboards in menu'),
+ path => '/Admin/Users/DashboardsInMenu.html?id=' . $id,
+ );
+ if ( RT->Config->Get('Crypt')->{'Enable'} ) {
+ $tabs->child( keys => title => loc('Private keys'), path => "/Admin/Users/Keys.html?id=" . $id );
}
+ $tabs->child( 'summary' => title => loc('User Summary'), path => "/User/Summary.html?id=" . $id );
}
}
@@ -323,10 +344,11 @@ my $build_admin_menu = sub {
my $obj = RT::Group->new( $session{'CurrentUser'} );
$obj->Load($id);
- if ( $obj and $obj->id ) {
+ if ( $obj and $obj->id ) {
my $tabs = PageMenu();
$tabs->child( basics => title => loc('Basics'), path => "/Admin/Groups/Modify.html?id=" . $obj->id );
$tabs->child( members => title => loc('Members'), path => "/Admin/Groups/Members.html?id=" . $obj->id );
+ $tabs->child( memberships => title => loc('Memberships'), path => "/Admin/Groups/Memberships.html?id=" . $obj->id );
$tabs->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Groups/GroupRights.html?id=" . $obj->id );
$tabs->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Groups/UserRights.html?id=" . $obj->id );
$tabs->child( history => title => loc('History'), path => "/Admin/Groups/History.html?id=" . $obj->id );
@@ -342,23 +364,78 @@ my $build_admin_menu = sub {
if ( $obj and $obj->id ) {
my $tabs = PageMenu();
- $tabs->child( basics => title => loc('Basics'), path => "/Admin/CustomFields/Modify.html?id=".$id );
- $tabs->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/CustomFields/GroupRights.html?id=" . $id );
- $tabs->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/CustomFields/UserRights.html?id=" . $id );
- $tabs->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/CustomFields/Objects.html?id=" . $id );
+ $tabs->child( basics => title => loc('Basics'), path => "/Admin/CustomFields/Modify.html?id=".$id );
+ $tabs->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/CustomFields/GroupRights.html?id=" . $id );
+ $tabs->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/CustomFields/UserRights.html?id=" . $id );
+ unless ( $obj->IsOnlyGlobal ) {
+ $tabs->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/CustomFields/Objects.html?id=" . $id );
+ }
}
}
}
- if ( $request_path =~ m{^/Admin/Global/(Scrip|Template)s?\.html} ) {
- my $type = $1;
- my $tabs = PageMenu();
+ if ( $request_path =~ m{^/Admin/Scrips/} ) {
+ if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/ ) {
+ my $id = $m->request_args->{'id'};
+ my $obj = RT::Scrip->new( $session{'CurrentUser'} );
+ $obj->Load($id);
+
+ my $tabs = PageMenu();
+
+ my ( $admin_cat, $create_path_arg, $from_query_param );
+ my $from_arg = $DECODED_ARGS->{'From'} || q{};
+ my ($from_queue) = $from_arg =~ /^(\d+)$/;
+ if ( $from_queue ) {
+ $admin_cat = "Queues/Scrips.html?id=$from_queue";
+ $create_path_arg = "?Queue=$from_queue";
+ $from_query_param = "&From=$from_queue";
+ }
+ elsif ( $from_arg eq 'Global' ) {
+ $admin_cat = 'Global/Scrips.html';
+ $create_path_arg = '?Global=1';
+ $from_query_param = '&From=Global';
+ }
+ else {
+ $admin_cat = 'Scrips';
+ $from_query_param = $create_path_arg = q{};
+ }
+ my $scrips = $tabs->child( scrips => title => loc('Scrips'), path => "/Admin/${admin_cat}" );
+ $scrips->child( select => title => loc('Select'), path => "/Admin/${admin_cat}" );
+ $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html${create_path_arg}" );
- # With only two elements, swapping between dropdown and menu is kinda dumb
- # In the glorious future this should be cleaner.
+ $tabs->child( basics => title => loc('Basics') => path => "/Admin/Scrips/Modify.html?id=" . $id . $from_query_param );
+ $tabs->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/Scrips/Objects.html?id=" . $id . $from_query_param );
+ }
+ elsif ( $request_path =~ m{^/Admin/Scrips/(index\.html)?$} ) {
+ PageMenu->child( select => title => loc('Select') => path => "/Admin/Scrips/" );
+ PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html" );
+ }
+ elsif ( $request_path =~ m{^/Admin/Scrips/Create\.html$} ) {
+ my ($queue) = $DECODED_ARGS->{'Queue'} && $DECODED_ARGS->{'Queue'} =~ /^(\d+)$/;
+ my $global_arg = $DECODED_ARGS->{'Global'};
+ if ($queue) {
+ PageMenu->child( select => title => loc('Select') => path => "/Admin/Queues/Scrips.html?id=$queue" );
+ PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html?Queue=$queue" );
+ } elsif ($global_arg) {
+ PageMenu->child( select => title => loc('Select') => path => "/Admin/Global/Scrips.html" );
+ PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html?Global=1" );
+ } else {
+ PageMenu->child( select => title => loc('Select') => path => "/Admin/Scrips" );
+ PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html" );
+ }
+ }
+ }
+
+ if ( $request_path =~ m{^/Admin/Global/Scrips\.html} ) {
+ my $tabs = PageMenu();
+ $tabs->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" );
+ $tabs->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Global=1" );
+ }
- $tabs->child( select => title => loc('Select'), path => "/Admin/Global/${type}s.html" );
- $tabs->child( create => title => loc('Create'), path => "/Admin/Global/${type}.html?Create=1" );
+ if ( $request_path =~ m{^/Admin/Global/Templates?\.html} ) {
+ my $tabs = PageMenu();
+ $tabs->child( select => title => loc('Select'), path => "/Admin/Global/Templates.html" );
+ $tabs->child( create => title => loc('Create'), path => "/Admin/Global/Template.html?Create=1" );
}
if ( $request_path =~ m{^/Admin/Articles/Classes/} ) {
@@ -386,24 +463,40 @@ my $build_admin_menu = sub {
}
};
-
my $build_main_nav = sub {
PageWidgets()->child( simple_search => raw_html => $m->scomp('SimpleSearch') );
PageWidgets()->child( create_ticket => raw_html => $m->scomp('CreateTicket') );
my $home = Menu->child( home => title => loc('Homepage'), path => '/' );
- # We explicitly exclude superusers; otherwise the dashboards for
- # groups you're not in (but can see the dashboards of by dint of
- # being a superuser) would push the useful ones from the groups
- # you're actually in off of the stack.
- my @dashboards = $m->comp("/Dashboards/Elements/ListOfDashboards", IncludeSuperuserGroups => 0);
- my $limit = 7;
+ unless ($session{'dashboards_in_menu'}) {
+ my $dashboards_in_menu = $session{CurrentUser}->UserObj->Preferences(
+ 'DashboardsInMenu',
+ {},
+ );
+
+ unless ($dashboards_in_menu->{dashboards}) {
+ my ($default_dashboards) =
+ RT::System->new( $session{'CurrentUser'} )
+ ->Attributes
+ ->Named('DashboardsInMenu');
+ if ($default_dashboards) {
+ $dashboards_in_menu = $default_dashboards->Content;
+ }
+ }
- my $more = 0;
- if ( @dashboards > $limit ) {
- $more = 1;
- splice @dashboards, $limit;
+ $session{'dashboards_in_menu'} = $dashboards_in_menu->{dashboards} || [];
+ }
+
+ my @dashboards;
+ for my $id ( @{$session{'dashboards_in_menu'}} ) {
+ my $dash = RT::Dashboard->new( $session{CurrentUser} );
+ my ( $status, $msg ) = $dash->LoadById($id);
+ if ( $status ) {
+ push @dashboards, $dash;
+ } else {
+ $RT::Logger->warning( "Failed to load dashboard $id: $msg" );
+ }
}
my $dashes = Menu()->child('home');
@@ -414,24 +507,34 @@ my $build_main_nav = sub {
path => '/Dashboards/' . $dash->id . '/' . $dash->Name
);
}
-
- $dashes->child( more => title => loc('All Dashboards'), path => 'Dashboards/index.html' );
}
+ $dashes->child( edit => title => loc('Update This Menu'), path => 'Prefs/DashboardsInMenu.html' );
+ $dashes->child( more => title => loc('All Dashboards'), path => 'Dashboards/index.html' );
my $dashboard = RT::Dashboard->new( $session{CurrentUser} );
if ( $dashboard->CurrentUserCanCreateAny ) {
$dashes->child('dashboard_create' => title => loc('New Dashboard'), path => "/Dashboards/Modify.html?Create=1" );
}
- my $tickets = Menu->child( search => title => loc('Tickets'), path => '/Search/Build.html' );
+ my $search = Menu->child( search => title => loc('Search'), path => '/Search/Simple.html' );
+
+ my $tickets = $search->child( tickets => title => loc('Tickets'), path => '/Search/Build.html' );
$tickets->child( simple => title => loc('Simple Search'), path => "/Search/Simple.html" );
$tickets->child( new => title => loc('New Search'), path => "/Search/Build.html?NewQuery=1" );
+ $search->child( articles => title => loc('Articles'), path => "/Articles/Article/Search.html" )
+ if $session{CurrentUser}->HasRight( Right => 'ShowArticlesMenu', Object => RT->System );
+
+ $search->child( users => title => loc('Users'), path => "/User/Search.html" );
+
+ if ($session{CurrentUser}->HasRight( Right => 'ShowArticlesMenu', Object => RT->System )) {
+ my $articles = Menu->child( articles => title => loc('Articles'), path => "/Articles/index.html");
+ $articles->child( articles => title => loc('Overview'), path => "/Articles/index.html" );
+ $articles->child( topics => title => loc('Topics'), path => "/Articles/Topics.html" );
+ $articles->child( create => title => loc('Create'), path => "/Articles/Article/PreCreate.html" );
+ $articles->child( search => title => loc('Search'), path => "/Articles/Article/Search.html" );
+ }
my $tools = Menu->child( tools => title => loc('Tools'), path => '/Tools/index.html' );
- my $articles = $tools->child( articles => title => loc('Articles'), path => "/Articles/index.html");
- $articles->child( articles => title => loc('Overview'), path => "/Articles/index.html" );
- $articles->child( search => title => loc('Search'), path => "/Articles/Article/Search.html" );
- $articles->child( topics => title => loc('Topics'), path => "/Articles/Topics.html" );
$tools->child( my_day =>
title => loc('My Day'),
@@ -447,12 +550,6 @@ my $build_main_nav = sub {
);
}
- $tools->child( offline =>
- title => loc('Offline'),
- description => loc('Create tickets offline'),
- path => '/Tools/Offline.html',
- );
-
if ( $session{'CurrentUser'}->HasRight( Right => 'ShowApprovalsTab', Object => RT->System ) ) {
$tools->child( approval =>
title => loc('Approval'),
@@ -463,7 +560,7 @@ my $build_main_nav = sub {
if ( $session{'CurrentUser'}->HasRight( Right => 'ShowConfigTab', Object => RT->System ) )
{
- $build_admin_menu->($tools);
+ $build_admin_menu->(Menu());
}
my $username = '<span class="current-user">'
@@ -472,6 +569,7 @@ my $build_main_nav = sub {
my $about_me = Menu->child( 'preferences' =>
title => loc('Logged in as [_1]', $username),
escape_title => 0,
+ path => '/User/Summary.html?id=' . $session{CurrentUser}->id,
sort_order => 99,
);
@@ -479,10 +577,14 @@ my $build_main_nav = sub {
if ( $session{'CurrentUser'}->UserObj
&& $session{'CurrentUser'}->HasRight( Right => 'ModifySelf', Object => RT->System )) {
my $settings = $about_me->child( settings => title => loc('Settings'), path => '/Prefs/Other.html' );
- $settings->child( options => title => loc('Options'), path => '/Prefs/Other.html' );
+ $settings->child( options => title => loc('Preferences'), path => '/Prefs/Other.html' );
$settings->child( about_me => title => loc('About me'), path => '/User/Prefs.html' );
$settings->child( search_options => title => loc('Search options'), path => '/Prefs/SearchOptions.html' );
$settings->child( myrt => title => loc('RT at a glance'), path => '/Prefs/MyRT.html' );
+ $settings->child( dashboards_in_menu =>
+ title => loc('Dashboards in menu'),
+ path => '/Prefs/DashboardsInMenu.html',
+ );
$settings->child( quicksearch => title => loc('Quick search'), path => '/Prefs/Quicksearch.html' );
my $search_menu = $settings->child( 'saved-searches' => title => loc('Saved Searches') );
@@ -500,8 +602,8 @@ my $build_main_nav = sub {
}
}
if ( $session{'CurrentUser'}->Name
- && ( !RT->Config->Get('WebExternalAuth')
- || RT->Config->Get('WebFallbackToInternalAuth') )) {
+ && ( !RT->Config->Get('WebRemoteUserAuth')
+ || RT->Config->Get('WebFallbackToRTLogin') )) {
$about_me->child( logout => title => loc('Logout'), path => '/NoAuth/Logout.html' );
}
if ( $request_path =~ m{^/Dashboards/(\d+)?}) {
@@ -539,7 +641,7 @@ my $build_main_nav = sub {
$tabs->child( history => title => loc('History'), path => "/Ticket/History.html?id=" . $id );
my %can = %{ $obj->CurrentUser->PrincipalObj->HasRights( Object => $obj ) };
- $can{'_ModifyOwner'} = $can{'OwnTicket'} || $can{'TakeTicket'} || $can{'StealTicket'};
+ $can{'_ModifyOwner'} = $obj->CurrentUserCanSetOwner();
my $can = sub {
unless ($_[0] eq 'ExecuteCode') {
return $can{$_[0]} || $can{'SuperUser'};
@@ -591,7 +693,7 @@ my $build_main_nav = sub {
&& $obj->HasUnresolvedDependencies;
my $current = $obj->Status;
- my $lifecycle = $obj->QueueObj->Lifecycle;
+ my $lifecycle = $obj->LifecycleObj;
my $i = 1;
foreach my $info ( $lifecycle->Actions($current) ) {
my $next = $info->{'to'};
@@ -606,42 +708,31 @@ my $build_main_nav = sub {
my $action = $info->{'update'} || '';
my $url = '/Ticket/';
- if ($action) {
- $url .= "Update.html?"
- . $query_string->(
- Action => $action,
- DefaultStatus => $next,
- id => $id,
- );
- } else {
- $url .= "Display.html?"
- . $query_string->(
- Status => $next,
- id => $id,
- );
- }
+ $url .= "Update.html?". $query_string->(
+ $action
+ ? (Action => $action)
+ : (SubmitTicket => 1, Status => $next),
+ DefaultStatus => $next,
+ id => $id,
+ );
my $key = $info->{'label'} || ucfirst($next);
$actions->child( $key => title => loc( $key ), path => $url);
}
- if ( $can->('OwnTicket') ) {
- if ( $obj->OwnerObj->Id == RT->Nobody->id
- && ( $can->('ModifyTicket') or $can->('TakeTicket') ) ) {
- $actions->child( take => title => loc('Take'), path => "/Ticket/Display.html?Action=Take;id=" . $id );
- }
-
- elsif ( $obj->OwnerObj->id != RT->Nobody->id
- && $obj->OwnerObj->id != $session{CurrentUser}->id
- && ( $can->('ModifyTicket') or $can->('StealTicket') ) ) {
- $actions->child( steal => title => loc('Steal'), path => "/Ticket/Display.html?Action=Steal;id=" . $id );
- }
+ my ($can_take, $tmsg) = $obj->CurrentUserCanSetOwner( Type => 'Take' );
+ my ($can_steal, $smsg) = $obj->CurrentUserCanSetOwner( Type => 'Steal' );
+ if ( $can_take ){
+ $actions->child( take => title => loc('Take'), path => "/Ticket/Display.html?Action=Take;id=" . $id );
+ }
+ elsif ( $can_steal ){
+ $actions->child( steal => title => loc('Steal'), path => "/Ticket/Display.html?Action=Steal;id=" . $id );
}
# TODO needs a "Can extract article into a class applied to this queue" check
$actions->child( 'extract-article' =>
title => loc('Extract Article'),
path => "/Articles/Article/ExtractIntoClass.html?Ticket=".$obj->id,
- );
+ ) if $session{CurrentUser}->HasRight( Right => 'ShowArticlesMenu', Object => RT->System );
if ( defined $session{"tickets"} ) {
# we have to update session data if we get new ItemMap
@@ -653,7 +744,7 @@ my $build_main_nav = sub {
$session{"tickets"}->PrepForSerialization();
}
- my $search = Menu()->child('search');
+ my $search = Menu()->child('search')->child('tickets');
# Don't display prev links if we're on the first ticket
if ( $item_map->{$id}->{prev} ) {
$search->child( first =>
@@ -684,7 +775,7 @@ my $build_main_nav = sub {
&& $DECODED_ARGS->{'q'} )
)
{
- my $search = Menu()->child('search');
+ my $search = Menu()->child('search')->child('tickets');
my $args = '';
my $has_query = '';
my $current_search = $session{"CurrentSearchHash"} || {};
@@ -844,11 +935,22 @@ my $build_main_nav = sub {
}
+ if ( $request_path =~ m{^/User/(Summary|History)\.html} ) {
+ if (PageMenu()->child('summary')) {
+ # Already set up from having AdminUser and ShowConfigTab;
+ # but rename "Basics" to "Edit" in this context
+ PageMenu()->child( 'basics' )->title( loc('Edit') );
+ } elsif ( $session{'CurrentUser'}->HasRight( Object => $RT::System, Right => 'ShowUserHistory' ) ) {
+ PageMenu()->child( display => title => loc('Summary'), path => '/User/Summary.html?id=' . $DECODED_ARGS->{'id'} );
+ PageMenu()->child( history => title => loc('History'), path => '/User/History.html?id=' . $DECODED_ARGS->{'id'} );
+ }
+ }
+
if ( $request_path =~ /^\/(?:index.html|$)/ ) {
PageMenu()->child( edit => title => loc('Edit'), path => '/Prefs/MyRT.html' );
}
- $m->callback( CallbackName => 'Privileged' );
+ $m->callback( CallbackName => 'Privileged', Path => $request_path );
};
my $build_selfservice_nav = sub {
@@ -890,8 +992,8 @@ my $build_selfservice_nav = sub {
}
if ( $session{'CurrentUser'}->Name
- && ( !RT->Config->Get('WebExternalAuth')
- || RT->Config->Get('WebFallbackToInternalAuth') )) {
+ && ( !RT->Config->Get('WebRemoteUserAuth')
+ || RT->Config->Get('WebFallbackToRTLogin') )) {
$about_me->child( logout => title => loc('Logout'), path => '/NoAuth/Logout.html' );
}
@@ -901,7 +1003,7 @@ my $build_selfservice_nav = sub {
PageWidgets->child( goto => raw_html => $m->scomp('/SelfService/Elements/GotoTicket') );
- $m->callback( CallbackName => 'SelfService' );
+ $m->callback( CallbackName => 'SelfService', Path => $request_path );
};
diff --git a/rt/share/html/Elements/TicketList b/rt/share/html/Elements/TicketList
index 4bade49..b252af6 100644
--- a/rt/share/html/Elements/TicketList
+++ b/rt/share/html/Elements/TicketList
@@ -46,11 +46,15 @@
%#
%# END BPS TAGGED BLOCK }}}
<%INIT>
+RT->Deprecated(
+ Remove => "4.4",
+ Instead => "/Elements/CollectionList",
+);
$m->comp(
- '/Elements/CollectionList',
- %ARGS,
- Class => 'RT::Tickets'
- );
+ '/Elements/CollectionList',
+ %ARGS,
+ Class => 'RT::Tickets'
+ );
</%INIT>
<%ARGS>
$Collection => undef
diff --git a/rt/share/html/Elements/TitleBox b/rt/share/html/Elements/TitleBox
index 3bb0d03..8d80146 100644
--- a/rt/share/html/Elements/TitleBox
+++ b/rt/share/html/Elements/TitleBox
@@ -48,5 +48,6 @@
<&| /Widgets/TitleBox, %ARGS &><% $m->content |n%></&>
<%init>
# For compatibility with 3.4
+RT->Deprecated( Remove => 4.4, Instead => "/Widgets/TitleBox" );
# $m->comp('/Widgets/TitleBox', %ARGS ); Doesn't actually work
</%init>
diff --git a/rt/share/html/Elements/TitleBoxEnd b/rt/share/html/Elements/TitleBoxEnd
index bf5ef42..8b52b07 100644
--- a/rt/share/html/Elements/TitleBoxEnd
+++ b/rt/share/html/Elements/TitleBoxEnd
@@ -47,5 +47,6 @@
%# END BPS TAGGED BLOCK }}}
<%init>
# For compatibility with 3.4
+RT->Deprecated( Remove => 4.4, Instead => "/Widgets/TitleBoxEnd" );
$m->comp('/Widgets/TitleBoxEnd', %ARGS );
</%init>
diff --git a/rt/share/html/Elements/TitleBoxStart b/rt/share/html/Elements/TitleBoxStart
index 15671aa..c639d19 100644
--- a/rt/share/html/Elements/TitleBoxStart
+++ b/rt/share/html/Elements/TitleBoxStart
@@ -47,5 +47,6 @@
%# END BPS TAGGED BLOCK }}}
<%init>
# For compatibility with 3.4
+RT->Deprecated( Remove => 4.4, Instead => "/Widgets/TitleBoxStart" );
$m->comp('/Widgets/TitleBoxStart', %ARGS );
</%init>
diff --git a/rt/share/html/Elements/ValidateCustomFields b/rt/share/html/Elements/ValidateCustomFields
index 55c1fce..7c8edde 100644
--- a/rt/share/html/Elements/ValidateCustomFields
+++ b/rt/share/html/Elements/ValidateCustomFields
@@ -48,38 +48,50 @@
<%INIT>
my ($valid, @res) = (1, ());
$CustomFields->GotoFirstItem;
+
+my $CFArgs = _ParseObjectCustomFieldArgs( $ARGSRef )->{ref($Object)}{$Object->Id || 0} || {};
+
while ( my $CF = $CustomFields->Next ) {
- my $field = $NamePrefix . $CF->Id . "-Value";
+ my $submitted = $CFArgs->{$CF->Id};
+ # Pick the first grouping
+ $submitted = $submitted ? $submitted->{(keys %$submitted)[0]} : {};
- my $value;
- if ($ARGSRef->{"${field}s-Magic"} and exists $ARGSRef->{"${field}s"}) {
- $value = $ARGSRef->{"${field}s"};
+ # If we don't have a value and we don't see the Magic, then we're not
+ # submitting a field.
+ next if not $ValidateUnsubmitted
+ and not exists $submitted->{"Value"}
+ and not exists $submitted->{"Upload"}
+ and not exists $submitted->{"Values"}
+ and not $submitted->{"Values-Magic"};
- # We only validate Single Combos -- multis can never be user input
- next if ref $value;
- }
- else {
- $value = $ARGSRef->{$field};
- }
- $m->notes(('Field-' . $CF->Id) => $value);
+ # We only validate Single Combos -- multis can never be user input
+ next if $submitted->{"Values-Magic"} and exists $submitted->{"Values"}
+ and ref $submitted->{"Values"};
- my @values = ();
- if ( ref $value eq 'ARRAY' ) {
- @values = @$value;
- } elsif ( $CF->Type =~ /text/i ) {
- @values = ($value);
- } else {
- @values = split /\r*\n/, ( defined $value ? $value : '');
- }
- @values = grep $_ ne '',
- map {
- s/\r+\n/\n/g;
- s/^\s+//;
- s/\s+$//;
- $_;
+ $m->notes(('Field-' . $CF->Id) => $submitted->{Values} // $submitted->{Value});
+
+ my @values = _NormalizeObjectCustomFieldValue(
+ CustomField => $CF,
+ Value => ($submitted->{Values} // $submitted->{Value} // $submitted->{Upload}),
+ );
+ if ($CF->Type =~ /^Date(?:Time)?$/) {
+ if (not @values) {
+ my $values = $Object->CustomFieldValues($CF->Id);
+ while (my $ocfv = $values->Next) {
+ push @values, $ocfv->Content;
+ }
}
- grep defined, @values;
- @values = ('') unless @values;
+ @values = grep {
+ my $DateObj = RT::Date->new ( $session{'CurrentUser'} );
+ $DateObj->Set(
+ Format => 'unknown',
+ Value => $_,
+ ($CF->Type eq "Date" ? (Timezone => 'utc') : ())
+ );
+ $DateObj->IsSet
+ } @values;
+ }
+ push @values, '' unless @values;
for my $value( @values ) {
if ($value) {
@@ -87,7 +99,7 @@ while ( my $CF = $CustomFields->Next ) {
my ($ok, $msg) = $CF->_CanonicalizeValue( $ref );
unless ($ok) {
$m->notes( ( 'InvalidField-' . $CF->Id ) => $msg );
- push @res, $msg;
+ push @res, $CF->Name .': '. $msg;
$valid = 0;
}
}
@@ -96,7 +108,7 @@ while ( my $CF = $CustomFields->Next ) {
my $msg = loc("Input must match [_1]", $CF->FriendlyPattern);
$m->notes( ('InvalidField-' . $CF->Id) => $msg );
- push @res, $msg;
+ push @res, $CF->Name .': '. $msg;
$valid = 0;
}
}
@@ -104,7 +116,8 @@ $m->notes('ValidFields', $valid);
return wantarray? ($valid, @res): $valid;
</%INIT>
<%ARGS>
+$Object => RT::Ticket->new( $session{'CurrentUser'})
$CustomFields
$ARGSRef
-$NamePrefix => "Object-RT::Ticket--CustomField-"
+$ValidateUnsubmitted => 0
</%ARGS>