<h1><&|/l&>Global Find/Replace in Tickets</&></h1>
<form action="Replace.html" method="post">
<label><&|/l&>Find</&>: <input name="Find" value="<% $Find %>" /></label><br />
<label><&|/l&>Replace</&>: <input name="Replace" value="<% $Replace %>" /></label><br />
% foreach my $type (qw( code approval ticket )) {
<label><input name="Type" type="radio" value="<% $type %>"
    <% ($type eq $Type) ? 'checked' : '' %>
/><% ucfirst($type) %></label>
% }
<input type="submit">
</form>
% return if !length $Find;
<hr>
% if (length $Replace) {
<&|/l&>Replaced</&>:
% } else {
<&|/l&>Found</&>:
% }
<% "@{[map '#'.$_, @found]}" || loc('None') %>
<%INIT>
my $Tickets = RT::Tickets->new($session{CurrentUser});
$Tickets->Limit( FIELD => 'Type', VALUE => $Type );
$Tickets->Limit( FIELD => 'Status', VALUE => 'new' );
$Tickets->Limit( FIELD => 'ContentType', VALUE => 'text/plain' );
my @found;
if (length $Find) {
    while (my $Ticket = $Tickets->Next) {
        my $Attachment = eval { $Ticket->Transactions->First->Attachments->First } or next;
        my $content = $Attachment->Content;
        index($content, $Find) >= 0 or next;
        push @found, $Ticket->Id;
        next if !length($Replace);
        $content =~ s/\Q$Find\E/$Replace/g;

        if ( $Attachment->ContentEncoding eq 'none' || ! $Attachment->ContentEncoding ) {
            # do nothing
        }
        elsif ( $Attachment->ContentEncoding eq 'base64' ) {
       	    Encode::_utf8_off($content);
            $content = MIME::Base64::encode_base64($content);
        }
        elsif ( $Attachment->ContentEncoding eq 'quoted-printable' ) {
       	    Encode::_utf8_off($content);
            $content = MIME::QuotedPrint::encode($content);
        }

        $Attachment->_Set( Field => 'Content', Value => $content );
    }
}
</%INIT>
<%ARGS>
$Find => undef
$Replace => undef
$Type => 'code'
</%ARGS>
