1 %# BEGIN BPS TAGGED BLOCK {{{
5 %# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
6 %# <sales@bestpractical.com>
8 %# (Except where explicitly superseded by other copyright notices)
13 %# This work is made available to you under the terms of Version 2 of
14 %# the GNU General Public License. A copy of that license should have
15 %# been provided with this software, but in any event can be snarfed
18 %# This work is distributed in the hope that it will be useful, but
19 %# WITHOUT ANY WARRANTY; without even the implied warranty of
20 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 %# General Public License for more details.
23 %# You should have received a copy of the GNU General Public License
24 %# along with this program; if not, write to the Free Software
25 %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 %# 02110-1301 or visit their web page on the internet at
27 %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 %# CONTRIBUTION SUBMISSION POLICY:
32 %# (The following paragraph is not intended to limit the rights granted
33 %# to you to modify and distribute this software under the terms of
34 %# the GNU General Public License and is only of importance to you if
35 %# you choose to contribute your changes and enhancements to the
36 %# community by submitting them to Best Practical Solutions, LLC.)
38 %# By intentionally submitting any modifications, corrections or
39 %# derivatives to this work, or any other work intended for use with
40 %# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 %# you are the copyright holder for those contributions and you grant
42 %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 %# royalty-free, perpetual, license to use, copy, create derivative
44 %# works based on those contributions, and sublicense and distribute
45 %# those contributions and any derivatives thereof.
47 %# END BPS TAGGED BLOCK }}}
49 use Regexp::Common qw(URI);
53 RT::Interface::Web::EscapeUTF8( \$content );
60 return $escaper->($args{value});
64 $args{value} = $escaper->($args{value});
65 my $result = qq{[<a target="new" href="$args{value}">}. loc('Open URL') .qq{</a>]};
66 return $args{value} . qq{ <span class="clickylink">$result</span>};
68 url_overwrite => sub {
70 $args{value} = $escaper->($args{value});
71 my $result = qq{<a target="new" href="$args{value}">$args{value}</a>};
72 return qq{<span class="clickylink">$result</span>};
79 regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
83 name => "httpurl_overwrite",
84 regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
85 action => "url_overwrite",
91 for my $rec( @types ) {
92 return $rec->{action}->(
94 all_matches => [ $args{value}, $1, $2, $3, $4, $5, $6, $7, $8, $9 ],
95 ) if $args{value} =~ $rec->{regex};
99 my $cache; # only defined via callback
101 # Hook to add more Clicky types
102 # XXX Have to have Page argument, as Mason gets caller wrong in Callback?
103 # This happens as we are in <%ONCE> block
105 CallbackPage => "/Elements/MakeClicky",
107 actions => \%actions,
115 $active{$_}++ for RT->Config->Get('Active_MakeClicky');
116 @types = grep $active{$_->{name}}, @types;
118 # Build up the whole match
119 my $regexp = join "|", map $_->{regex}, @types;
121 # Make sure we have a default
122 $actions{default} ||= sub {};
124 # Anchor the regexes and look up the actions
125 foreach my $type ( @types ) {
126 $type->{regex} = qr/^$type->{regex}$/;
127 $type->{action} = $actions{$type->{action}} || $actions{default};
136 return unless defined $$content;
137 if ( defined $cache ) {
138 my $cached_content = $cache->(fetch => $content);
139 if ( $cached_content ) {
140 RT->Logger->debug("Found MakeClicky cache");
141 $$content = $cached_content;
147 RT::Interface::Web::EscapeUTF8( $content ) unless $html;
152 while ( $$content =~ /($regexp)/gsio ) {
154 next if $` =~ /href=(?:"|")$/;
155 my $skipped_len = pos($$content) - $pos - length($match);
156 if ( $skipped_len > 0 ) {
159 $plain = substr( $$content, $pos, $skipped_len );
162 $plain = $escaper->( substr( $$content, $pos, $skipped_len ) )
164 substr( $$content, $pos, $skipped_len ) = $plain;
165 $pos += length($plain);
167 my $plain = $handle->(
170 all_matches => [ $1, $2, $3, $4, $5, $6, $7, $8, $9 ],
172 substr( $$content, $pos, length($match) ) = $plain;
173 pos($$content) = ( $pos += length($plain) );
176 substr( $$content, $pos ) = $escaper->( substr( $$content, $pos ) ) unless
177 ($pos == length $$content) || $html;
180 $cache->(store => $content) if defined $cache;