rt 4.0.23
[freeside.git] / rt / share / html / Elements / MakeClicky
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
6 %#                                          <sales@bestpractical.com>
7 %#
8 %# (Except where explicitly superseded by other copyright notices)
9 %#
10 %#
11 %# LICENSE:
12 %#
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
16 %# from www.gnu.org.
17 %#
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.
22 %#
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.
28 %#
29 %#
30 %# CONTRIBUTION SUBMISSION POLICY:
31 %#
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.)
37 %#
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.
46 %#
47 %# END BPS TAGGED BLOCK }}}
48 <%ONCE>
49 use Regexp::Common qw(URI);
50
51 my $escaper = sub {
52     my $content = shift;
53     RT::Interface::Web::EscapeUTF8( \$content );
54     return $content;
55 };
56
57 my %actions = (
58     default => sub {
59         my %args = @_;
60         return $escaper->($args{value});
61     },
62     url => sub {
63         my %args = @_;
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>};
67     },
68     url_overwrite => sub {
69         my %args = @_;
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>};
73     },
74 );
75
76 my @types = (
77     {
78         name   => "httpurl",
79         regex  => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
80         action => "url",
81     },
82     {
83         name   => "httpurl_overwrite",
84         regex  => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
85         action => "url_overwrite",
86     },
87 );
88
89 my $handle = sub {
90     my %args = @_;
91     for my $rec( @types ) {
92         return $rec->{action}->(
93             %args,
94             all_matches => [ $args{value}, $1, $2, $3, $4, $5, $6, $7, $8, $9 ],
95         ) if $args{value} =~ $rec->{regex};
96     }
97 };
98
99 my $cache; # only defined via callback
100
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
104 $m->callback(
105     CallbackPage => "/Elements/MakeClicky",
106     types        => \@types,
107     actions      => \%actions,
108     handle       => \$handle,
109     cache        => \$cache,
110 );
111
112
113 # Filter
114 my %active;
115 $active{$_}++ for RT->Config->Get('Active_MakeClicky');
116 @types = grep $active{$_->{name}}, @types;
117
118 # Build up the whole match
119 my $regexp = join "|", map $_->{regex}, @types;
120
121 # Make sure we have a default
122 $actions{default} ||= sub {};
123
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};
128 }
129
130 </%ONCE>
131 <%ARGS>
132 $content => undef
133 $html => undef
134 </%ARGS>
135 <%INIT>
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;
142         return;
143     }
144 }
145
146 unless ( $regexp ) {
147     RT::Interface::Web::EscapeUTF8( $content ) unless $html;
148     return;
149 }
150
151 my $pos = 0;
152 while ( $$content =~ /($regexp)/gsio ) {
153     my $match = $1;
154     next if $` =~ /href=(?:&quot;|")$/;
155     my $skipped_len = pos($$content) - $pos - length($match);
156     if ( $skipped_len > 0 ) {
157         my $plain;
158         if ( $html ) {
159             $plain = substr( $$content, $pos, $skipped_len );
160         }
161         else {
162             $plain = $escaper->( substr( $$content, $pos, $skipped_len ) )
163         }
164         substr( $$content, $pos, $skipped_len ) = $plain;
165         $pos += length($plain);
166     }
167     my $plain = $handle->(
168         %ARGS, 
169         value => $match,
170         all_matches => [ $1, $2, $3, $4, $5, $6, $7, $8, $9 ],
171     );
172     substr( $$content, $pos, length($match) ) = $plain;
173     pos($$content) = ( $pos += length($plain) );
174
175 }
176 substr( $$content, $pos ) = $escaper->( substr( $$content, $pos ) ) unless
177 ($pos == length $$content) || $html;
178
179 pos($$content) = 0;
180 $cache->(store => $content) if defined $cache;
181
182 </%INIT>