| 1293 |
dpurdie |
1 |
###############################################################################
|
|
|
2 |
# Codestriker: Copyright (c) 2001, 2002 David Sitsky. All rights reserved.
|
|
|
3 |
# sits@users.sourceforge.net
|
|
|
4 |
#
|
|
|
5 |
# This program is free software; you can redistribute it and modify it under
|
|
|
6 |
# the terms of the GPL.
|
|
|
7 |
|
|
|
8 |
# Collection of routines for creating HTTP responses, including headers and
|
|
|
9 |
# error indications.
|
|
|
10 |
|
|
|
11 |
package Codestriker::Http::Response;
|
|
|
12 |
|
|
|
13 |
use strict;
|
|
|
14 |
use Codestriker::Http::Cookie;
|
|
|
15 |
use HTML::Entities ();
|
|
|
16 |
|
|
|
17 |
# Constructor for this class. Indicate that the response header hasn't been
|
|
|
18 |
# generated yet.
|
|
|
19 |
sub new($$) {
|
|
|
20 |
my ($type, $query) = @_;
|
|
|
21 |
|
|
|
22 |
my $self = {};
|
|
|
23 |
$self->{header_generated} = 0;
|
|
|
24 |
$self->{query} = $query;
|
|
|
25 |
$self->{format} = $query->param('format');
|
|
|
26 |
$self->{action} = $query->param('action');
|
|
|
27 |
return bless $self, $type;
|
|
|
28 |
}
|
|
|
29 |
|
|
|
30 |
# Return the query object associated with the response.
|
|
|
31 |
sub get_query($) {
|
|
|
32 |
my ($self) = @_;
|
|
|
33 |
|
|
|
34 |
return $self->{query};
|
|
|
35 |
}
|
|
|
36 |
|
|
|
37 |
# Generate the initial HTTP response header, with the initial HTML header.
|
|
|
38 |
# Most of the input parameters are used for storage into the user's cookie.
|
|
|
39 |
sub generate_header {
|
|
|
40 |
|
|
|
41 |
my ($self, %params) = @_;
|
|
|
42 |
|
|
|
43 |
my $topic = undef;
|
|
|
44 |
my $topic_title = "";
|
|
|
45 |
my $email = "";
|
|
|
46 |
my $reviewers = "";
|
|
|
47 |
my $cc = "";
|
|
|
48 |
my $mode = "";
|
|
|
49 |
my $tabwidth = "";
|
|
|
50 |
my $repository = "";
|
|
|
51 |
my $projectid = "";
|
|
|
52 |
my $load_anchor = "";
|
|
|
53 |
my $topicsort = "";
|
|
|
54 |
my $fview = -1;
|
|
|
55 |
|
|
|
56 |
my $reload = $params{reload};
|
|
|
57 |
my $cache = $params{cache};
|
|
|
58 |
|
|
|
59 |
$load_anchor = $params{load_anchor};
|
|
|
60 |
$load_anchor = "" if ! defined $load_anchor;
|
|
|
61 |
|
|
|
62 |
# If the header has already been generated, do nothing.
|
|
|
63 |
return if ($self->{header_generated});
|
|
|
64 |
$self->{header_generated} = 1;
|
|
|
65 |
my $query = $self->{query};
|
|
|
66 |
|
|
|
67 |
# Set the topic and title parameters.
|
|
|
68 |
$topic = $params{topic};
|
|
|
69 |
$topic_title = $params{topic_title};
|
|
|
70 |
|
|
|
71 |
# Set the fview parameter if defined.
|
|
|
72 |
$fview = $params{fview} if defined $params{fview};
|
|
|
73 |
|
|
|
74 |
# Set the cookie in the HTTP header for the $email, $cc, $reviewers and
|
|
|
75 |
# $tabwidth parameters.
|
|
|
76 |
my %cookie = ();
|
|
|
77 |
|
|
|
78 |
if (!exists $params{email} || $params{email} eq "") {
|
|
|
79 |
$email = Codestriker::Http::Cookie->get_property($query, 'email');
|
|
|
80 |
}
|
|
|
81 |
else {
|
|
|
82 |
$email = $params{email};
|
|
|
83 |
}
|
|
|
84 |
|
|
|
85 |
if (!exists $params{reviewers} || $params{reviewers} eq "") {
|
|
|
86 |
$reviewers = Codestriker::Http::Cookie->get_property($query,
|
|
|
87 |
'reviewers');
|
|
|
88 |
}
|
|
|
89 |
else {
|
|
|
90 |
$reviewers = $params{reviewers};
|
|
|
91 |
}
|
|
|
92 |
|
|
|
93 |
if (!exists $params{cc} || $params{cc} eq "") {
|
|
|
94 |
$cc = Codestriker::Http::Cookie->get_property($query, 'cc');
|
|
|
95 |
}
|
|
|
96 |
else {
|
|
|
97 |
$cc = $params{cc};
|
|
|
98 |
}
|
|
|
99 |
|
|
|
100 |
if (!exists $params{tabwidth} || $params{tabwidth} eq "") {
|
|
|
101 |
$tabwidth = Codestriker::Http::Cookie->get_property($query,
|
|
|
102 |
'tabwidth');
|
|
|
103 |
}
|
|
|
104 |
else {
|
|
|
105 |
$tabwidth = $params{tabwidth};
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
if (!exists $params{mode} || $params{mode} eq "") {
|
|
|
109 |
$mode = Codestriker::Http::Cookie->get_property($query, 'mode');
|
|
|
110 |
}
|
|
|
111 |
else {
|
|
|
112 |
$mode = $params{mode};
|
|
|
113 |
}
|
|
|
114 |
|
|
|
115 |
if (!exists $params{repository} || $params{repository} eq "") {
|
|
|
116 |
$repository = Codestriker::Http::Cookie->get_property($query,
|
|
|
117 |
'repository');
|
|
|
118 |
}
|
|
|
119 |
else {
|
|
|
120 |
$repository = $params{repository};
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
if (!exists $params{projectid} || $params{projectid} eq "") {
|
|
|
124 |
$projectid = Codestriker::Http::Cookie->get_property($query,
|
|
|
125 |
'projectid');
|
|
|
126 |
}
|
|
|
127 |
else {
|
|
|
128 |
$projectid = $params{projectid};
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
if (!exists $params{topicsort} || $params{topicsort} eq "") {
|
|
|
132 |
$topicsort = Codestriker::Http::Cookie->get_property($query,
|
|
|
133 |
'topicsort');
|
|
|
134 |
}
|
|
|
135 |
else {
|
|
|
136 |
$topicsort = $params{topicsort};
|
|
|
137 |
}
|
|
|
138 |
|
|
|
139 |
$cookie{'email'} = $email if $email ne "";
|
|
|
140 |
$cookie{'reviewers'} = $reviewers if $reviewers ne "";
|
|
|
141 |
$cookie{'cc'} = $cc if $cc ne "";
|
|
|
142 |
$cookie{'tabwidth'} = $tabwidth if $tabwidth ne "";
|
|
|
143 |
$cookie{'mode'} = $mode if $mode ne "";
|
|
|
144 |
$cookie{'repository'} = $repository if $repository ne "";
|
|
|
145 |
$cookie{'projectid'} = $projectid if $projectid ne "";
|
|
|
146 |
$cookie{'topicsort'} = $topicsort if $topicsort ne "";
|
|
|
147 |
|
|
|
148 |
my $cookie_obj = Codestriker::Http::Cookie->make($query, \%cookie);
|
|
|
149 |
|
|
|
150 |
# This logic is taken from cvsweb. There is _reason_ behind this logic...
|
|
|
151 |
# Basically mozilla supports gzip regardless even though some versions
|
|
|
152 |
# don't state this. IE claims it does, but doesn't support it. Using
|
|
|
153 |
# the gzip binary technique doesn't work apparently under mod_perl.
|
|
|
154 |
|
|
|
155 |
# Determine if the client browser is capable of handled compressed HTML.
|
|
|
156 |
eval {
|
|
|
157 |
require Compress::Zlib;
|
|
|
158 |
};
|
|
|
159 |
my $output_compressed = 0;
|
|
|
160 |
my $has_zlib = !$@;
|
|
|
161 |
my $browser = $ENV{'HTTP_USER_AGENT'};
|
|
|
162 |
my $can_compress = ($Codestriker::use_compression &&
|
|
|
163 |
((defined($ENV{'HTTP_ACCEPT_ENCODING'})
|
|
|
164 |
&& $ENV{'HTTP_ACCEPT_ENCODING'} =~ m|gzip|)
|
|
|
165 |
|| $browser =~ m%^Mozilla/3%)
|
|
|
166 |
&& ($browser !~ m/MSIE/)
|
|
|
167 |
&& !(defined($ENV{'MOD_PERL'}) && !$has_zlib));
|
|
|
168 |
|
|
|
169 |
# Output the appropriate header if compression is allowed to the client.
|
|
|
170 |
if ($can_compress &&
|
|
|
171 |
($has_zlib || ($Codestriker::gzip ne "" &&
|
|
|
172 |
open(GZIP, "| $Codestriker::gzip -1 -c")))) {
|
|
|
173 |
if ($cache) {
|
|
|
174 |
print $query->header(-cookie=>$cookie_obj,
|
|
|
175 |
-content_encoding=>'x-gzip',
|
|
|
176 |
-charset=>"UTF-8",
|
|
|
177 |
-vary=>'Accept-Encoding');
|
|
|
178 |
} else {
|
|
|
179 |
print $query->header(-cookie=>$cookie_obj,
|
|
|
180 |
-expires=>'+1d',
|
|
|
181 |
-charset=>"UTF-8",
|
|
|
182 |
-cache_control=>'no-store',
|
|
|
183 |
-pragma=>'no-cache',
|
|
|
184 |
-content_encoding=>'x-gzip',
|
|
|
185 |
-vary=>'Accept-Encoding');
|
|
|
186 |
}
|
|
|
187 |
|
|
|
188 |
# Flush header output, and switch STDOUT to GZIP.
|
|
|
189 |
$| = 1; $| = 0;
|
|
|
190 |
if ($has_zlib) {
|
|
|
191 |
tie *GZIP, __PACKAGE__, \*STDOUT;
|
|
|
192 |
}
|
|
|
193 |
select(GZIP);
|
|
|
194 |
$output_compressed = 1;
|
|
|
195 |
} else {
|
|
|
196 |
# Make sure the STDOUT encoding is set to UTF8. Not needed
|
|
|
197 |
# when the data is being sent as compressed bytes.
|
|
|
198 |
binmode STDOUT, ':utf8';
|
|
|
199 |
if ($cache) {
|
|
|
200 |
print $query->header(-cookie=>$cookie_obj,
|
|
|
201 |
-charset=>"UTF-8");
|
|
|
202 |
} else {
|
|
|
203 |
print $query->header(-cookie=>$cookie_obj,
|
|
|
204 |
-expires=>'+1d',
|
|
|
205 |
-charset=>"UTF-8",
|
|
|
206 |
-cache_control=>'no-store',
|
|
|
207 |
-pragma=>'no-cache');
|
|
|
208 |
}
|
|
|
209 |
}
|
|
|
210 |
|
|
|
211 |
my $title = "Codestriker";
|
|
|
212 |
if (defined $topic_title && $topic_title ne "") {
|
|
|
213 |
$title .= ": \"$topic_title\"";
|
|
|
214 |
}
|
|
|
215 |
$title = HTML::Entities::encode($title);
|
|
|
216 |
|
|
|
217 |
# Generate the URL to the codestriker CSS file.
|
|
|
218 |
my $codestriker_css;
|
|
|
219 |
if (defined $Codestriker::codestriker_css &&
|
|
|
220 |
$Codestriker::codestriker_css ne "") {
|
|
|
221 |
$codestriker_css = $Codestriker::codestriker_css;
|
|
|
222 |
} else {
|
|
|
223 |
$codestriker_css = $query->url();
|
|
|
224 |
$codestriker_css =~ s/\/[\w\-]+\/codestriker\.pl/\/codestrikerhtml\/codestriker\.css/;
|
|
|
225 |
}
|
|
|
226 |
|
|
|
227 |
my $overlib_js = $codestriker_css;
|
|
|
228 |
$overlib_js =~ s/codestriker.css/overlib.js/o;
|
|
|
229 |
my $overlib_centerpopup_js = $codestriker_css;
|
|
|
230 |
$overlib_centerpopup_js =~ s/codestriker.css/overlib_centerpopup.js/o;
|
|
|
231 |
my $overlib_draggable_js = $codestriker_css;
|
|
|
232 |
$overlib_draggable_js =~ s/codestriker.css/overlib_draggable.js/o;
|
|
|
233 |
my $xbdhtml_js = $codestriker_css;
|
|
|
234 |
$xbdhtml_js =~ s/codestriker.css/xbdhtml.js/o;
|
|
|
235 |
my $codestriker_js = $codestriker_css;
|
|
|
236 |
$codestriker_js =~ s/codestriker.css/codestriker.js/o;
|
|
|
237 |
|
|
|
238 |
# Print the basic HTML header header, with the inclusion of the scripts.
|
|
|
239 |
print '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">';
|
|
|
240 |
print "\n";
|
|
|
241 |
print '<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">';
|
|
|
242 |
print "\n";
|
|
|
243 |
print "<head><title>$title</title>\n";
|
|
|
244 |
print "<link rel=\"stylesheet\" type=\"text/css\" href=\"$codestriker_css\" />\n";
|
|
|
245 |
print "<script src=\"$overlib_js\" type=\"text/javascript\"></script>\n";
|
|
|
246 |
print "<script src=\"$overlib_centerpopup_js\" type=\"text/javascript\"></script>\n";
|
|
|
247 |
print "<script src=\"$overlib_draggable_js\" type=\"text/javascript\"></script>\n";
|
|
|
248 |
print "<script src=\"$xbdhtml_js\" type=\"text/javascript\"></script>\n";
|
|
|
249 |
print "<script src=\"$codestriker_js\" type=\"text/javascript\"></script>\n";
|
|
|
250 |
print "<script type=\"text/javascript\">\n";
|
|
|
251 |
print " var cs_load_anchor = '$load_anchor';\n";
|
|
|
252 |
print " var cs_reload = $reload;\n";
|
|
|
253 |
print " var cs_topicid = $topic->{topicid};\n" if defined $topic;
|
|
|
254 |
print " var cs_email = '$email';\n" if defined $email;
|
|
|
255 |
print " var cs_css = '$codestriker_css';\n";
|
|
|
256 |
print " var cs_xbdhtml_js = '$xbdhtml_js';\n";
|
|
|
257 |
|
|
|
258 |
# Now output all of the comment metric information.
|
|
|
259 |
print " var cs_metric_data = new Array();\n";
|
|
|
260 |
my $i = 0;
|
|
|
261 |
foreach my $metric_config (@{ $Codestriker::comment_state_metrics }) {
|
|
|
262 |
print " cs_metric_data[$i] = new Object();\n";
|
|
|
263 |
print " cs_metric_data[$i].name = '" .
|
|
|
264 |
$metric_config->{name} . "';\n";
|
|
|
265 |
print " cs_metric_data[$i].values = new Array();\n";
|
|
|
266 |
my $j = 0;
|
|
|
267 |
foreach my $value (@{ $metric_config->{values} }) {
|
|
|
268 |
print " cs_metric_data[$i].values[$j] = '$value';\n";
|
|
|
269 |
$j++;
|
|
|
270 |
}
|
|
|
271 |
if (defined $metric_config->{default_value}) {
|
|
|
272 |
print " cs_metric_data[$i].default_value = '" .
|
|
|
273 |
$metric_config->{default_value} . "';\n";
|
|
|
274 |
}
|
|
|
275 |
$i++;
|
|
|
276 |
}
|
|
|
277 |
|
|
|
278 |
# Check that the external javascript files were loaded, and if not
|
|
|
279 |
# output an error message. This is usually due to a
|
|
|
280 |
# misconfiguration.
|
|
|
281 |
print " if ('function' != typeof window.add_comment_html) {\n";
|
|
|
282 |
print " alert('Oh oh... can\\'t find codestriker.js, please check your apache config.');\n";
|
|
|
283 |
print " }\n";
|
|
|
284 |
|
|
|
285 |
print "</script>\n";
|
|
|
286 |
|
|
|
287 |
# Output the comment declarations if the $comments array is defined.
|
|
|
288 |
my $comments = $params{comments};
|
|
|
289 |
if (defined $comments) {
|
|
|
290 |
print generate_comment_declarations($topic, $comments, $query,
|
|
|
291 |
$fview, $tabwidth);
|
|
|
292 |
}
|
|
|
293 |
|
|
|
294 |
# Write an HTML comment indicating if response was sent compressed or not.
|
|
|
295 |
$self->{output_compressed} = $output_compressed;
|
|
|
296 |
print "\n<!-- Source was" . (!$output_compressed ? " not" : "") .
|
|
|
297 |
" sent compressed. -->\n";
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
# Return the javascript code necessary to support viewing/modification of
|
|
|
301 |
# comments.
|
|
|
302 |
sub generate_comment_declarations
|
|
|
303 |
{
|
|
|
304 |
my ($topic, $comments, $query, $fview, $tabwidth) = @_;
|
|
|
305 |
|
|
|
306 |
# The output html to return.
|
|
|
307 |
my $html = "";
|
|
|
308 |
|
|
|
309 |
# Build a hash from filenumber|fileline|new -> comment array, to record
|
|
|
310 |
# what comments are associated with what locations. Also record the
|
|
|
311 |
# order of comment_locations found.
|
|
|
312 |
my %comment_hash = ();
|
|
|
313 |
my @comment_locations = ();
|
|
|
314 |
for (my $i = 0; $i <= $#$comments; $i++) {
|
|
|
315 |
my $comment = $$comments[$i];
|
|
|
316 |
my $key = $comment->{filenumber} . "|" . $comment->{fileline} . "|" .
|
|
|
317 |
$comment->{filenew};
|
|
|
318 |
if (! exists $comment_hash{$key}) {
|
|
|
319 |
push @comment_locations, $key;
|
|
|
320 |
}
|
|
|
321 |
push @{ $comment_hash{$key} }, $comment;
|
|
|
322 |
}
|
|
|
323 |
|
|
|
324 |
# Precompute the overlib HTML for each comment location.
|
|
|
325 |
$html .= "\n<script language=\"JavaScript\" type=\"text/javascript\">\n";
|
|
|
326 |
|
|
|
327 |
# Add the reviewers for the review here.
|
|
|
328 |
$html .= " var topic_reviewers = '" . $topic->{reviewers} . "';\n";
|
|
|
329 |
|
|
|
330 |
# Now record all the comments made so far in the topic.
|
|
|
331 |
$html .= " var comment_text = new Array();\n";
|
|
|
332 |
$html .= " var comment_hash = new Array();\n";
|
|
|
333 |
$html .= " var comment_metrics = new Array();\n";
|
|
|
334 |
my $index;
|
|
|
335 |
for ($index = 0; $index <= $#comment_locations; $index++) {
|
|
|
336 |
|
|
|
337 |
# Contains the overlib HTML text.
|
|
|
338 |
my $overlib_html = "";
|
|
|
339 |
|
|
|
340 |
# Determine what the previous and next comment locations are.
|
|
|
341 |
my $previous = undef;
|
|
|
342 |
my $next = undef;
|
|
|
343 |
if ($index > 0) {
|
|
|
344 |
$previous = $comment_locations[$index-1];
|
|
|
345 |
}
|
|
|
346 |
if ($index < $#comment_locations) {
|
|
|
347 |
$next = $comment_locations[$index+1];
|
|
|
348 |
}
|
|
|
349 |
|
|
|
350 |
# Compute the previous link if required.
|
|
|
351 |
my $current_url = $query->self_url();
|
|
|
352 |
if (defined $previous && $previous =~ /^(\-?\d+)|\-?\d+|\d+$/o) {
|
|
|
353 |
my $previous_fview = $1;
|
|
|
354 |
my $previous_index = $index - 1;
|
|
|
355 |
my $previous_url = $current_url;
|
|
|
356 |
$previous_url =~ s/fview=\d+/fview=$previous_fview/o if $fview != -1;
|
|
|
357 |
$previous_url .= '#' . $previous;
|
|
|
358 |
$overlib_html .= "<a href=\"javascript:window.location=\\'$previous_url\\'; ";
|
|
|
359 |
if ($fview == -1 || $fview == $previous_fview) {
|
|
|
360 |
$overlib_html .= "overlib(comment_text[$previous_index], STICKY, DRAGGABLE, ALTCUT, FIXX, getEltPageLeft(getElt(\\'c$previous_index\\')), FIXY, getEltPageTop(getElt(\\'c$previous_index\\'))); ";
|
|
|
361 |
}
|
|
|
362 |
$overlib_html .= "void(0);\">Previous</a>";
|
|
|
363 |
}
|
|
|
364 |
|
|
|
365 |
# Compute the next link if required.
|
|
|
366 |
if (defined $next && $next =~ /^(\-?\d+)|\-?\d+|\d+$/o) {
|
|
|
367 |
my $next_fview = $1;
|
|
|
368 |
$overlib_html .= " | " if defined $previous;
|
|
|
369 |
my $next_index = $index + 1;
|
|
|
370 |
my $next_url = $current_url;
|
|
|
371 |
$next_url =~ s/fview=\d+/fview=$next_fview/o if $fview != -1;
|
|
|
372 |
$next_url .= '#' . $next;
|
|
|
373 |
$overlib_html .= "<a href=\"javascript:window.location=\\'$next_url\\'; ";
|
|
|
374 |
if ($fview == -1 || $fview == $next_fview) {
|
|
|
375 |
$overlib_html .= "overlib(comment_text[$next_index], STICKY, DRAGGABLE, ALTCUT, FIXX, getEltPageLeft(getElt(\\'c$next_index\\')), FIXY, getEltPageTop(getElt(\\'c$next_index\\'))); ";
|
|
|
376 |
}
|
|
|
377 |
$overlib_html .= "void(0);\">Next</a>";
|
|
|
378 |
}
|
|
|
379 |
if (defined $previous || defined $next) {
|
|
|
380 |
$overlib_html .= " | ";
|
|
|
381 |
}
|
|
|
382 |
|
|
|
383 |
# Add an add comment link.
|
|
|
384 |
my $key = $comment_locations[$index];
|
|
|
385 |
$key =~ /^(\-?\d+)\|(\-?\d+)\|(\d+)$/o;
|
|
|
386 |
if (!Codestriker::topic_readonly($topic->{topic_state})) {
|
|
|
387 |
$overlib_html .= "<a href=\"javascript:add_comment_tooltip($1,$2,$3)" .
|
|
|
388 |
"; void(0);\">Add Comment<\\/a> | ";
|
|
|
389 |
}
|
|
|
390 |
|
|
|
391 |
# Add a close link.
|
|
|
392 |
$overlib_html .= "<a href=\"javascript:hideElt(getElt(\\'overDiv\\')); void(0);\">Close<\\/a><p>";
|
|
|
393 |
|
|
|
394 |
# Create the actual comment text.
|
|
|
395 |
my @comments = @{ $comment_hash{$key} };
|
|
|
396 |
|
|
|
397 |
for (my $i = 0; $i <= $#comments; $i++) {
|
|
|
398 |
my $comment = $comments[$i];
|
|
|
399 |
|
|
|
400 |
# Need to format the data appropriately for HTML display.
|
|
|
401 |
my $data = HTML::Entities::encode($comment->{data});
|
|
|
402 |
$data =~ s/\\/\\\\/mgo;
|
|
|
403 |
$data =~ s/\'/\\\'/mgo;
|
|
|
404 |
$data =~ s/\n/<br>/mgo;
|
|
|
405 |
$data =~ s/ \s+/' ' x (length($&)-1)/emgo;
|
|
|
406 |
$data = Codestriker::Http::Render::tabadjust($tabwidth, $data, 1);
|
|
|
407 |
|
|
|
408 |
# Show each comment with the author and date in bold.
|
|
|
409 |
$overlib_html .= "<b>Comment from $comment->{author} ";
|
|
|
410 |
$overlib_html .= "on $comment->{date}<\\/b><br>";
|
|
|
411 |
$overlib_html .= "$data";
|
|
|
412 |
|
|
|
413 |
# Add a newline at the end if required.
|
|
|
414 |
if ($i < $#comments &&
|
|
|
415 |
substr($overlib_html, length($overlib_html)-4, 4) ne '<br>') {
|
|
|
416 |
$overlib_html .= '<br>';
|
|
|
417 |
}
|
|
|
418 |
}
|
|
|
419 |
|
|
|
420 |
$html .= " comment_text[$index] = '$overlib_html';\n";
|
|
|
421 |
$html .= " comment_hash['" . $comment_locations[$index] .
|
|
|
422 |
"'] = $index;\n";
|
|
|
423 |
|
|
|
424 |
# Store the current metric values for this comment.
|
|
|
425 |
$html .= " comment_metrics[$index] = new Array();\n";
|
|
|
426 |
my $comment_metrics = $comments[0]->{metrics};
|
|
|
427 |
foreach my $metric_config (@{ $Codestriker::comment_state_metrics }) {
|
|
|
428 |
my $value = $comment_metrics->{$metric_config->{name}};
|
|
|
429 |
$value = "" unless defined $value;
|
|
|
430 |
$html .= " comment_metrics[${index}]['" .
|
|
|
431 |
$metric_config->{name} . "'] = '" . $value . "';\n";
|
|
|
432 |
}
|
|
|
433 |
|
|
|
434 |
}
|
|
|
435 |
$html .= "</script>\n";
|
|
|
436 |
|
|
|
437 |
# Now declare the CSS positional elements for each comment location.
|
|
|
438 |
$html .= "<style type=\"text/css\">\n";
|
|
|
439 |
for (my $i = 0; $i <= $#$comments; $i++) {
|
|
|
440 |
$html .= '#c' . $i . ' { position: absolute; }' . "\n";
|
|
|
441 |
}
|
|
|
442 |
$html .= "</style>\n";
|
|
|
443 |
|
|
|
444 |
# Return the generated HTML.
|
|
|
445 |
return $html;
|
|
|
446 |
}
|
|
|
447 |
|
|
|
448 |
|
|
|
449 |
# Close the response, which only requires work if we are dealing with
|
|
|
450 |
# compressed streams.
|
|
|
451 |
sub generate_footer($) {
|
|
|
452 |
my ($self) = @_;
|
|
|
453 |
|
|
|
454 |
if ($self->{output_compressed}) {
|
|
|
455 |
select(STDOUT);
|
|
|
456 |
close(GZIP);
|
|
|
457 |
untie *GZIP;
|
|
|
458 |
}
|
|
|
459 |
}
|
|
|
460 |
|
|
|
461 |
# Generate an error page response if bad input was passed in.
|
|
|
462 |
sub error($$) {
|
|
|
463 |
my ($self, $error_message) = @_;
|
|
|
464 |
|
|
|
465 |
my $query = $self->{query};
|
|
|
466 |
|
|
|
467 |
# Check if the expected format is XML.
|
|
|
468 |
if (defined $self->{format} && $self->{format} eq "xml") {
|
|
|
469 |
print $query->header(-content_type=>'text/xml');
|
|
|
470 |
print "<?xml version=\"1.0\" encoding=\"UTF-8\" " .
|
|
|
471 |
"standalone=\"yes\"?>\n";
|
|
|
472 |
print "<response><method>" . $self->{action} . "</method>" .
|
|
|
473 |
"<result>" . HTML::Entities::encode($error_message) .
|
|
|
474 |
"</result></response>\n";
|
|
|
475 |
}
|
|
|
476 |
else {
|
|
|
477 |
if (! $self->{header_generated}) {
|
|
|
478 |
print $query->header,
|
|
|
479 |
$query->start_html(-title=>'Codestriker error',
|
|
|
480 |
-bgcolor=>'white');
|
|
|
481 |
}
|
|
|
482 |
|
|
|
483 |
print $query->p, "<FONT COLOR='red'>$error_message</FONT>", $query->p;
|
|
|
484 |
print $query->end_html();
|
|
|
485 |
|
|
|
486 |
$self->generate_footer();
|
|
|
487 |
}
|
|
|
488 |
|
|
|
489 |
exit;
|
|
|
490 |
}
|
|
|
491 |
|
|
|
492 |
# Implement a gzipped file handle via the Compress:Zlib compression
|
|
|
493 |
# library. This code was stolen from CVSweb.
|
|
|
494 |
|
|
|
495 |
sub MAGIC1() { 0x1f }
|
|
|
496 |
sub MAGIC2() { 0x8b }
|
|
|
497 |
sub OSCODE() { 3 }
|
|
|
498 |
|
|
|
499 |
sub TIEHANDLE {
|
|
|
500 |
my ($class, $out) = @_;
|
|
|
501 |
my $level = Compress::Zlib::Z_BEST_COMPRESSION();
|
|
|
502 |
my $wbits = -Compress::Zlib::MAX_WBITS();
|
|
|
503 |
my ($d) = Compress::Zlib::deflateInit(-Level => $level,
|
|
|
504 |
-WindowBits => $wbits)
|
|
|
505 |
or return undef;
|
|
|
506 |
my ($o) = {
|
|
|
507 |
handle => $out,
|
|
|
508 |
dh => $d,
|
|
|
509 |
crc => 0,
|
|
|
510 |
len => 0,
|
|
|
511 |
};
|
|
|
512 |
my ($header) = pack("C10", MAGIC1, MAGIC2,
|
|
|
513 |
Compress::Zlib::Z_DEFLATED(),
|
|
|
514 |
0,0,0,0,0,0, OSCODE);
|
|
|
515 |
print {$o->{handle}} $header;
|
|
|
516 |
return bless($o, $class);
|
|
|
517 |
}
|
|
|
518 |
|
|
|
519 |
sub PRINT {
|
|
|
520 |
my ($o) = shift;
|
|
|
521 |
my ($buf) = join(defined $, ? $, : "",@_);
|
|
|
522 |
my ($len) = length($buf);
|
|
|
523 |
my ($compressed, $status) = $o->{dh}->deflate($buf);
|
|
|
524 |
print {$o->{handle}} $compressed if defined($compressed);
|
|
|
525 |
$o->{crc} = Compress::Zlib::crc32($buf, $o->{crc});
|
|
|
526 |
$o->{len} += $len;
|
|
|
527 |
return $len;
|
|
|
528 |
}
|
|
|
529 |
|
|
|
530 |
sub CLOSE {
|
|
|
531 |
my ($o) = @_;
|
|
|
532 |
return if !defined( $o->{dh});
|
|
|
533 |
my ($buf) = $o->{dh}->flush();
|
|
|
534 |
$buf .= pack("V V", $o->{crc}, $o->{len});
|
|
|
535 |
print {$o->{handle}} $buf;
|
|
|
536 |
undef $o->{dh};
|
|
|
537 |
}
|
|
|
538 |
|
|
|
539 |
sub DESTROY {
|
|
|
540 |
my ($o) = @_;
|
|
|
541 |
CLOSE($o);
|
|
|
542 |
}
|
|
|
543 |
|
|
|
544 |
1;
|