]> git.uio.no Git - usit-rt.git/blob - share/html/Elements/MakeClicky
8bd4ecf1b64f42328c83d849704b3bbb33c3dabe
[usit-rt.git] / share / html / Elements / MakeClicky
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2014 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::EscapeHTML( \$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         my $post = "";
65         $post = ")" if $args{value} !~ /\(/ and $args{value} =~ s/\)$//;
66         $args{value} = $escaper->($args{value});
67         my $result = qq{[<a target="new" href="$args{value}">}. loc('Open URL') .qq{</a>]};
68         return $args{value} . qq{ <span class="clickylink">$result</span>$post};
69     },
70     url_overwrite => sub {
71         my %args = @_;
72         my $post = "";
73         $post = ")" if $args{value} !~ /\(/ and $args{value} =~ s/\)$//;
74         $args{value} = $escaper->($args{value});
75         my $result = qq{<a target="new" href="$args{value}">$args{value}</a>};
76         return qq{<span class="clickylink">$result</span>$post};
77     },
78 );
79
80 my @types = (
81     {
82         name   => "httpurl",
83         regex  => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
84         action => "url",
85     },
86     {
87         name   => "httpurl_overwrite",
88         regex  => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}(?:#[^\s<]+)?(?<![.?!,;:])/,
89         action => "url_overwrite",
90     },
91 );
92
93 my $handle = sub {
94     my %args = @_;
95     for my $rec( @types ) {
96         return $rec->{action}->(
97             %args,
98             all_matches => [ $args{value}, $1, $2, $3, $4, $5, $6, $7, $8, $9 ],
99         ) if $args{value} =~ $rec->{regex};
100     }
101 };
102
103 my $cache; # only defined via callback
104
105 # Hook to add more Clicky types
106 # XXX Have to have Page argument, as Mason gets caller wrong in Callback?
107 # This happens as we are in <%ONCE> block
108 $m->callback(
109     CallbackPage => "/Elements/MakeClicky",
110     types        => \@types,
111     actions      => \%actions,
112     handle       => \$handle,
113     cache        => \$cache,
114 );
115
116
117 # Filter
118 my %active;
119 $active{$_}++ for RT->Config->Get('Active_MakeClicky');
120 @types = grep $active{$_->{name}}, @types;
121
122 # Build up the whole match
123 my $regexp = join "|", map $_->{regex}, @types;
124
125 # Make sure we have a default
126 $actions{default} ||= sub {};
127
128 # Anchor the regexes and look up the actions
129 foreach my $type ( @types ) {
130     $type->{regex}  = qr/^$type->{regex}$/;
131     $type->{action} = $actions{$type->{action}} || $actions{default};
132 }
133
134 </%ONCE>
135 <%ARGS>
136 $content => undef
137 $html => undef
138 </%ARGS>
139 <%INIT>
140 return unless defined $$content;
141 if ( defined $cache ) {
142     my $cached_content = $cache->(fetch => $content);
143     if ( $cached_content ) {
144         RT->Logger->debug("Found MakeClicky cache");
145         $$content = $cached_content;
146         return;
147     }
148 }
149
150 unless ( $regexp ) {
151     RT::Interface::Web::EscapeHTML( $content ) unless $html;
152     return;
153 }
154
155 my $pos = 0;
156 while ( $$content =~ /($regexp)/gsio ) {
157     my $match = $1;
158     next if $` =~ /\w+=(?:&quot;|")$/;
159     my $skipped_len = pos($$content) - $pos - length($match);
160     if ( $skipped_len > 0 ) {
161         my $plain;
162         if ( $html ) {
163             $plain = substr( $$content, $pos, $skipped_len );
164         }
165         else {
166             $plain = $escaper->( substr( $$content, $pos, $skipped_len ) )
167         }
168         substr( $$content, $pos, $skipped_len ) = $plain;
169         $pos += length($plain);
170     }
171     my $plain = $handle->(
172         %ARGS, 
173         value => $match,
174         all_matches => [ $1, $2, $3, $4, $5, $6, $7, $8, $9 ],
175     );
176     substr( $$content, $pos, length($match) ) = $plain;
177     pos($$content) = ( $pos += length($plain) );
178
179 }
180 substr( $$content, $pos ) = $escaper->( substr( $$content, $pos ) ) unless
181 ($pos == length $$content) || $html;
182
183 pos($$content) = 0;
184 $cache->(store => $content) if defined $cache;
185
186 </%INIT>