Commit cf6bfa49 authored by Chia-liang Kao's avatar Chia-liang Kao
Browse files

re-merge all changes on 3.7 after the test rename merge from tunis.

git-svn-id: svn+ssh://svn.bestpractical.com/svn/bps-public/rt/branches/3.7-EXPERIMENTAL@8417 e417ac7c-1bcc-0310-8ffa-8f5827389a85
parent bf08f2da
......@@ -52,17 +52,21 @@ rt-mailgate - Mail interface to RT3.
=cut
use strict;
use warnings;
use Getopt::Long;
use LWP::UserAgent;
use HTTP::Request::Common qw($DYNAMIC_FILE_UPLOAD);
$DYNAMIC_FILE_UPLOAD = 1;
use constant EX_TEMPFAIL => 75;
use constant BUFFER_SIZE => 8192;
my %opts;
GetOptions( \%opts, "queue=s", "action=s", "url=s", "jar=s", "help", "debug", "extension=s", "timeout=i" );
if ( $opts{help} ) {
if ( $opts{'help'} ) {
require Pod::Usage;
import Pod::Usage;
pod2usage("RT Mail Gateway\n");
......@@ -74,61 +78,63 @@ unless ( $opts{'url'} ) {
exit 1;
}
my $ua = LWP::UserAgent->new();
$ua->cookie_jar( { file => $opts{jar} } );
my $ua = new LWP::UserAgent;
$ua->cookie_jar( { file => $opts{'jar'} } ) if $opts{'jar'};
my %args = (
queue => $opts{queue},
action => $opts{action},
queue => $opts{'queue'},
action => ($opts{'action'} || 'correspond'),
SessionType => 'REST', # Surpress login box
);
# Read the message in from STDIN
$args{'message'} = do { local (@ARGV, $/); <> };
unless ( $args{message} =~ /\S/ ) {
print STDERR "$0: no message passed on STDIN!\n";
exit 0;
}
if ( ($opts{'extension'} || '') =~ /^(?:action|queue|ticket)$/i ) {
$args{ lc $opts{'extension'} } = $ENV{'EXTENSION'};
} elsif ( $opts{'extension'} && $ENV{'EXTENSION'} ) {
print STDERR "Value of the --extension argument is not action, queue or ticket"
.", but environment variable EXTENSION is also defined. The former is ignored.";
.", but environment variable EXTENSION is also defined. The former is ignored.\n";
}
# add ENV{'EXTENSION'} as X-RT-MailExtension to the message header
if ( my $value = ( $ENV{'EXTENSION'} || $opts{'extension'} ) ) {
# prepare value to avoid MIME format brakage
# prepare value to avoid MIME format breakage
# strip trailing newline symbols
$value =~ s/(\r*\n)+$//;
# make a correct multiline header field,
# with tabs in the beginning of each line
$value =~ s/(\r*\n)/$1\t/g;
$args{'message'} = "X-RT-Mail-Extension: $value\n"
. $args{'message'};
print $args{'message'};
$opts{'headers'} .= "X-RT-Mail-Extension: $value\n";
}
# Set up cookie here.
# Read the message in from STDIN
my %message = write_down_message();
unless( $message{'filename'} ) {
$args{'message'} = [
undef, '',
'Content-Type' => 'application/octet-stream',
Content => ${ $message{'content'} },
];
} else {
$args{'message'} = [
$message{'filename'}, '',
'Content-Type' => 'application/octet-stream',
];
}
my $full_url = $opts{'url'}. "/REST/1.0/NoAuth/mail-gateway";
warn "Connecting to $full_url" if $opts{'debug'};
print STDERR "$0: connecting to $full_url\n" if $opts{'debug'};
$ua->timeout(exists($opts{'timeout'}) ? $opts{'timeout'} : 180);
my $r = $ua->post( $full_url, {%args} );
$ua->timeout( exists( $opts{'timeout'} )? $opts{'timeout'}: 180 );
my $r = $ua->post( $full_url, \%args, Content_Type => 'form-data' );
check_failure($r);
my $content = $r->content;
warn $content if ($opts{debug});
print STDERR $content ."\n" if $opts{'debug'};
if ( $content !~ /^(ok|not ok)/ ) {
# It's not the server's fault if the mail is bogus. We just want to know that
# *something* came out of the server.
warn <<EOF;
print STDERR <<EOF;
RT server error.
The RT server which handled your email did not behave as expected. It
......@@ -137,16 +143,19 @@ said:
$content
EOF
exit EX_TEMPFAIL;
exit EX_TEMPFAIL;
}
exit;
END {
unlink $message{'filename'} if $message{'filename'};
}
sub check_failure {
my $r = shift;
return if $r->is_success();
return if $r->is_success;
# This ordinarily oughtn't to be able to happen, suggests a bug in RT.
# So only load these heavy modules when they're needed.
......@@ -154,17 +163,62 @@ sub check_failure {
require HTML::FormatText;
my $error = $r->error_as_HTML;
my $tree = HTML::TreeBuilder->new->parse($error);
my $tree = HTML::TreeBuilder->new->parse( $error );
$tree->eof;
# It'll be a cold day in hell before RT sends out bounces in HTML
my $formatter = HTML::FormatText->new( leftmargin => 0,
rightmargin => 50 );
warn $formatter->format($tree);
warn "This is $0 exiting because of an undefined server error" if ($opts{debug});
my $formatter = HTML::FormatText->new(
leftmargin => 0,
rightmargin => 50,
);
print STDERR $formatter->format( $tree );
print STDERR "\n$0: undefined server error\n" if $opts{'debug'};
exit EX_TEMPFAIL;
}
sub write_down_message {
use File::Temp qw(tempfile);
my ($fh, $filename) = tempfile('rt-mailgate-XXXX', SUFIX => '.bin');
unless( $fh ) {
print STDERR "$0: couldn't create temp file, using memory\n";
my $message = \do { local (@ARGV, $/); <> };
unless ( $$message =~ /\S/ ) {
print STDERR "$0: no message passed on STDIN\n";
exit 0;
}
$$message = $opts{'headers'} . $$message if $opts{'headers'};
return ( content => $message );
}
binmode $fh;
binmode \*STDIN;
print $fh $opts{'headers'} if $opts{'headers'};
my $buf; my $empty = 1;
while(1) {
my $status = read \*STDIN, $buf, BUFFER_SIZE;
unless ( defined $status ) {
print STDERR "$0: couldn't read message: $!\n";
exit EX_TEMPFAIL;
} elsif ( !$status ) {
last;
}
$empty = 0 if $buf =~ /\S/;
print $fh $buf;
};
close $fh;
if ( $empty ) {
print STDERR "$0: no message passed on STDIN\n";
exit 0;
}
print STDERR "$0: temp file is '$filename'\n" if $opts{'debug'};
return (filename => $filename);
}
=head1 SYNOPSIS
......
......@@ -438,6 +438,20 @@ You sent us a message that we cannot handle due to corrupted GnuPG signature or
}
}}
},
{ Queue => 0,
Name => "PasswordChange", # loc
Description =>
"Inform user that his password has been reset", # loc
Content => q{Subject: [{RT->Config->Get('rtname')}] Password reset
Greetings,
Someone at {$ENV{'REMOTE_ADDR'}} requested a password reset for you on {RT->Config->Get('WebURL')}
Your new password is:
{$NewPassword}
}
},
);
# }}}
......
%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
%# <jesse@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/copyleft/gpl.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 }}}
<& /Elements/ListActions, actions => \@actions &>
<form method="get" action="Templates.html">
<input type="hidden" class="hidden" name="id" value="<%$id%>" />
<input type="hidden" class="hidden" name="id" value="<% $id %>" />
% if ($Templates->Count == 0 ) {
% unless ( $Templates->Count ) {
<p><i><&|/l&>(No templates)</&></i></p>
% } else {
<table>
<tr>
<th>
<i><&|/l&>(Check box to delete)</&></i>
</th>
<th>
</th>
</tr>
% my $count;
% while (my $TemplateObj = $Templates->Next) {
<tr>
<td>
<input type="checkbox" class="checkbox" name="DeleteTemplate-<%$TemplateObj->Id%>" value="1" />
</td>
<td>
<a href="Template.html?Queue=<%$id%>&Template=<%$TemplateObj->id()%>">
<strong><% loc($TemplateObj->Name) %></strong></a>
<br /><% loc($TemplateObj->Description) %>
</td>
</tr>
% }
</table>
<& /Elements/CollectionList,
OrderBy => 'id',
Order => 'ASC',
BaseURL => 'Templates.html?',
%ARGS,
DisplayFormat => '__CheckBox.{DeleteTemplates}__,'. $Format,
Format => $Format,
Collection => $Templates,
AllowSorting => 1,
PassArguments => [qw(Format Rows Page Order OrderBy FindDisabledQueues)],
&>
% }
<& /Elements/Submit, Label => loc('Delete Template') &>
</form>
<%INIT>
my $Templates = RT::Templates->new($session{'CurrentUser'});
my $QueueObj = RT::Queue->new($session{'CurrentUser'});
my @actions;
my $dir_path = $m->request_comp->dir_path;
$Format ||= qq{'<a href="__WebPath__$dir_path/Template.html?Queue=$id&Template=__id__">__id__</a>/TITLE:#'}
.qq{,'<a href="__WebPath__$dir_path/Template.html?Queue=$id&Template=__id__">__Name__</a>/TITLE:Name'}
.qq{,'__Description__'};
if ($id) {
$QueueObj->Load($id);
}
my $QueueObj = RT::Queue->new( $session{'CurrentUser'} );
$QueueObj->Load( $id ) if $id;
if ($QueueObj->id) {
$Templates->LimitToQueue($id);
}
else {
$Templates->LimitToGlobal();
}
my $Templates = RT::Templates->new($session{'CurrentUser'});
if ( $QueueObj->id ) {
$Templates->LimitToQueue( $id );
}
else {
$Templates->LimitToGlobal;
}
# Now let callbacks add their extra limits
$m->callback( %ARGS, Templates => $Templates );
$Templates->RedoSearch;
# {{{ deal with deleting existing templates
# deal with deleting existing templates
my @actions;
# backwards compatibility, use DeleteTemplates array for this
foreach my $key (keys %ARGS) {
# {{{ if we're trying to delete the template
if ($key =~ /^DeleteTemplate-(\d+)/) {
my $id = $1;
my $TemplateObj = RT::Template->new($session{'CurrentUser'});
$TemplateObj->Load($id);
next unless $key =~ /^DeleteTemplate-(\d+)/;
push @DeleteTemplates, $1;
}
foreach my $id( @DeleteTemplates ) {
my $TemplateObj = RT::Template->new( $session{'CurrentUser'} );
$TemplateObj->Load( $id );
unless ( $TemplateObj->id ) {
push @actions, loc("Couldn't load template #[_1]", $id);
next;
}
my ($retval, $msg) = $TemplateObj->Delete;
if ($retval) {
push @actions, loc("Template deleted");
if ( $retval ) {
push @actions, loc("Template #[_1] deleted", $id);
}
else {
push @actions, $msg;
push @actions, $msg;
}
}
# }}}
}
# }}}
</%INIT>
<%ARGS>
$id => 0
$title => undef
$Move => undef
$Source => undef
$Template => undef
$Format => undef
@DeleteTemplates => ()
</%ARGS>
......@@ -64,7 +64,7 @@
% foreach my $uinfo( @{ $res{'info'}{'User'} } ) {
<tr><th><% loc('User (created - expire)') %>:</th>
<td><% $uinfo->{'String'} %>\
(<% $uinfo->{'Created'}->AsString( Time => 0 ) %> - \
(<% $uinfo->{'Created'}? $uinfo->{'Created'}->AsString( Time => 0 ): loc('never') %> - \
<% $uinfo->{'Expire'}? $uinfo->{'Expire'}->AsString( Time => 0 ): loc('never') %>)
</td></tr>
% }
......
......@@ -63,7 +63,7 @@
<%init>
my @actions;
my @items = map { [ "component-$_", $_ ] } sort RT->Config->Get('HomepageComponents');
my @items = map { [ "component-$_", $_ ] } sort @{ RT->Config->Get('HomepageComponents') };
my $sys = RT::System->new( $session{'CurrentUser'} );
# XXX: put this in savedsearches_to_portlet_items
for ( $m->comp( "/Search/Elements/SearchesForObject",
......
......@@ -121,14 +121,14 @@
<&|/l&>New Password</&>:
</td>
<td align="left">
<input type="password" name="Pass1" />
<input type="password" name="Pass1" autocomplete="off" />
</td>
</tr>
<tr><td align="right">
<&|/l&>Retype Password</&>:
</td>
<td>
<input type="password" name="Pass2" />
<input type="password" name="Pass2" autocomplete="off" />
</td>
</tr>
</table>
......
......@@ -83,7 +83,7 @@ if ($ARGS{Reset}) {
my ($default_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('HomepageSettings');
my $portlets = $UserObj->Preferences('HomepageSettings', $default_portlets ? $default_portlets->Content : {});
my %allowed_components = map {$_ => 1} RT->Config->Get('HomepageComponents');
my %allowed_components = map {$_ => 1} @{RT->Config->Get('HomepageComponents')};
my @items;
push @items, map {["component-$_", $_]} sort keys %allowed_components;
......
......@@ -50,67 +50,87 @@
current_subtab => 'Admin/Users/',
Title => loc('Select a user') &>
<h1><%$caption%></h1>
<h1><% $caption %></h1>
<p><&|/l&>Select a user</&>:</p>
<ul>
% unless ( $users->Count ) {
<li><em><&|/l&>No users matching search criteria found.</&></em></li>
% }
% } else {
<& /Elements/CollectionList,
OrderBy => 'Name',
Order => 'ASC',
Rows => 100,
BaseURL => RT->Config->Get('WebPath') .'/Admin/Users/index.html?',
%ARGS,
Format => $Format,
Collection => $users,
AllowSorting => 1,
PassArguments => [qw(Format Rows Page Order OrderBy UserString UserOp UserField IdLike EmailLike FindDisabledUsers)],
&>
% my @ids;
% while ( $user = $users->Next ) {
% push @ids, $user->Id;
<li><a href="Modify.html?id=<% $user->id %>"><& /Elements/ShowUser, User => $user &></a></li>
%}
</ul>
% if ( my $ids = join(',', @ids) ) {
<em>(<a href="<%RT->Config->Get('WebPath')%>/Download/Tabular/User/<% $ids %>/Users.tsv"><&|/l&>Download as a tab-delimited file</&></a>)</em><br />
% my $ids = join ',', map $_->id, @{ $users->ItemsArrayRef };
% if ( $ids ) {
<div align="right"><em>
(<a href="<%RT->Config->Get('WebPath')%>/Download/Tabular/User/<% $ids %>/Users.tsv">
<&|/l&>Download as a tab-delimited file</&>
</a>)</em></div>
% }
<br /><br />
<form method="post" action="<% RT->Config->Get('WebPath') %>/Admin/Users/index.html">
% }
<form method="post" action="<% RT->Config->Get('WebPath') %>/Admin/Users/index.html">
% foreach my $field( qw(Format Rows Page Order OrderBy) ) {
% next unless defined $ARGS{ $field } && length $ARGS{ $field };
<input type="hidden" name="<% $field %>" value="<% $ARGS{ $field } %>" />
% }
<&|/l&>Find people whose</&> <& /Elements/SelectUsers &><br />
<input type="checkbox" class="checkbox" name="FindDisabledUsers" value="1" /> <&|/l&>Include disabled users in search.</&>
<input type="checkbox" class="checkbox" name="FindDisabledUsers" value="1" <% $FindDisabledUsers? 'checked="checked"': '' %> />
<&|/l&>Include disabled users in search.</&>
<br />
<div align="right"><input type="submit" class="button" value="<&|/l&>Go!</&>" /></div>
</form>
<%INIT>
my ($user, $caption);
my $users = new RT::Users($session{'CurrentUser'});
my $caption;
my $users = RT::Users->new( $session{'CurrentUser'} );
$users->{'find_disabled_rows'} = 1 if $FindDisabledUsers;
if ($FindDisabledUsers) {
$users->{'find_disabled_rows'} = 1;
}
if (length $UserString) {
if ( length $UserString ) {
$caption = loc("Users matching search criteria");
if ($UserField =~ /^CustomField-(\d+)/) {
$users->LimitCustomField(
CUSTOMFIELD => $1,
OPERATOR => $UserOp,
VALUE => $UserString,
);
if ( $UserField =~ /^CustomField-(\d+)/ ) {
$users->LimitCustomField(
CUSTOMFIELD => $1,
OPERATOR => $UserOp,
VALUE => $UserString,
);
}
else {
$users->Limit(
FIELD => $UserField,
OPERATOR => $UserOp,
VALUE => $UserString,
);
$users->Limit(
FIELD => $UserField,
OPERATOR => $UserOp,
VALUE => $UserString,
);
}
}
else {
$caption = loc("Privileged users");
$users->LimitToPrivileged;
}
$Format ||= q{'<a href="__WebPath__/Admin/Users/Modify.html?id=__id__">__id__</a>/TITLE:#'}
.q{,'<a href="__WebPath__/Admin/Users/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
.q{,__RealName__, __EmailAddress__};
</%INIT>
<%ARGS>
$Format => undef,
$UserString => undef
$UserOp => '='
$UserField => 'Name'
$IdLike => undef
$EmailLike => undef
$FindDisabledUsers => 0
</%ARGS>
......@@ -92,10 +92,10 @@ foreach my $col ( @Format ) {
$m->out('<th class="collection-as-table">');
$title =~ s/^__(.*)__$/$1/o;
my $tmp = $m->comp(
"/Elements/$Class/ColumnMap",
Name => $title,
Attr => 'title',
my $tmp = $m->comp( '/Elements/ColumnMap',
Class => $Class,
Name => $title,
Attr => 'title',
);
unless( $tmp ) {
......
......@@ -88,9 +88,10 @@ foreach my $column ( @Format ) {
my $col = $1;
unless ( exists $ColumnMap->{ $col } ) {
$ColumnMap->{ $col } = $m->comp(
"/Elements/$Class/ColumnMap",
Name => $col,
Attr => 'value'
"/Elements/ColumnMap",
Class => $Class,
Name => $col,
Attr => 'value'
);
}
......
<%ARGS>
$Class => 'RT__Ticket'
$Name
$Attr => undef
</%ARGS>
<%ONCE>
# This is scary and should totally be refactored -- jesse
my $COLUMN_MAP = {
id => {
attribute => 'id',
align => 'right',
value => sub { return $_[0]->id }
},
Created => {
attribute => 'Created',
title => 'Created',
value => sub { return $_[0]->CreatedObj->AsString }
},
CreatedRelative => {
attribute => 'Created',
title => 'Created',
value => sub { return $_[0]->CreatedObj->AgeAsString }
},
CreatedBy => {
attribute => 'CreatedBy',
title => 'Created By',
value => sub { return $_[0]->CreatorObj->Name }
},
LastUpdated => {
attribute => 'LastUpdated',
title => 'Last Updated',
value => sub { return $_[0]->LastUpdatedObj->AsString }
},
LastUpdatedRelative => {
attribute => 'LastUpdated',
title => 'Last Updated',
value => sub { return $_[0]->LastUpdatedObj->AgeAsString }
},
LastUpdatedBy => {
attribute => 'LastUpdatedBy',
title => 'Last Updated By',