]> mj.ucw.cz Git - pciids.git/blob - PciIds/Html/Admin.pm
Fix escaping of characters in stock answers
[pciids.git] / PciIds / Html / Admin.pm
1 package PciIds::Html::Admin;
2 use strict;
3 use warnings;
4 use PciIds::Users;
5 use PciIds::Html::Util;
6 use PciIds::Html::Users;
7 use PciIds::Html::Forms;
8 use PciIds::Notifications;
9 use PciIds::Address;
10 use PciIds::Log;
11 use PciIds::Startup;
12 use Apache2::Const qw(:common :http);
13
14 sub safeEncode( $ ) {
15         my( $text ) = @_;
16         return encode( $text ) if defined $text;
17         return '';
18 }
19
20 sub mailEncode( $$ ) {
21         my( $email, $user ) = @_;
22         return '' unless defined $email;
23         ( $user ) = $email =~ /^(.*)@/ unless( defined $user );
24         return "<a href='mailto:$email'>".encode( $user )."</a>";
25 }
26
27 sub genHist( $$$$$$$$$$$ ) {
28         my( $class, $email, $login, $time, $name, $note, $disc, $selname, $selvalue, $delname, $delvalue ) = @_;
29         print "<tr class='$class'><td>";
30         print "<span class='author'>".mailEncode( $email, $login )."<br></span>" if( defined $email );
31         print "<span class='time'>".safeEncode( $time )."</span>";
32         print "<td>";
33         if( defined $name ) {
34                 if( $name eq '' ) {
35                         print "<span class='name'>Deletion request<br></span>";
36                 } else {
37                         print "<span class='name'>Name: ".encode( $name )."<br></span>";
38                 }
39         }
40         print "<span class='note'>Note: ".encode( $note )."<br></span>" if defined $note && $note ne '';
41         print safeEncode( $disc );
42         print "<td class='selects'><input type='radio' name='$selname' value='$selvalue'>\n";
43         print "<td class='deletes'><input type='checkbox' name='$delname' value='$delvalue'>\n" if defined $delname
44 }
45
46 sub genNewForm( $$ ) {
47         my( $num, $values ) = @_;
48         print "<tr class='newhistory'><td>";
49         if( @{$values} ) {
50                 print "<select id='ans-$num' name='ans-$num' onchange='answer( \"$num\" );'><option value='0'>Stock answers</option>";
51                 my $i = 0;
52                 foreach( @{$values} ) {
53                         $i ++;
54                         my( $name ) = @{$_};
55                         print "<option value='$i'>$name</option>";
56                 }
57                 print "</select>\n";
58         } else {
59                 print "No stock answers";
60         }
61         print "<td><span class='newname'>Name: <input type='text' name='name-$num'></span><span class='newnote'>Note: <input type='text' name='note-$num'></span><br>\n";
62         print "<textarea id='disc-$num' name='disc-$num'></textarea>\n";
63         print "<td><td class='deletes'><input type='checkbox' name='loc-$num-softdel' value='del'>\n";
64 }
65
66 sub loadStock() {
67         my @stock;
68         if( open ANS, "$directory/answers" ) {
69                 my( $name, $text, $lines );
70                 while( defined( $name = <ANS> ) ) {
71                         $lines = '';
72                         while( defined( $text = <ANS> ) ) {
73                                 chomp $text;
74                                 last if $text eq '';
75                                 $lines .= "\n" if $lines ne '';
76                                 $lines .= $text;
77                         }
78                         push @stock, [ encode( $name ), $lines ];
79                 }
80                 close ANS;
81         } else {
82                 print STDERR "Could not find stock answers.\n";
83         }
84         return \@stock;
85 }
86
87 sub genNewAdminForm( $$$$$ ) {
88         my( $req, $args, $tables, $error, $auth ) = @_;
89         my $address = PciIds::Address::new( $req->uri() );
90         my $prefix = $address->get();
91         my $limit = delete $args->{'limit'};
92         $prefix = '' if( $args->{'global'} );
93         my $caption = 'Administration '.( $args->{'global'} ? '(Global)' : '('.encode( $address->pretty() ).')' );
94         genHtmlHead( $req, $caption, undef );
95         my $glob = delete $args->{'global'};
96         my $moreButt = 'admin?limit='.( $limit ? int( $limit * 2 ) : 160 ).buildExcept( 'action', $args );
97         $moreButt .= '?global='.$glob if defined $glob;
98         genCustomHead( $req, $args, $address, $caption, [ $address->canAddItem() ? [ 'Add item', 'newitem' ] : (), $address->canDiscuss() ? [ 'Discuss', 'newhistory' ] : (), $glob ? [ 'Local', 'admin'.buildExcept( 'action', $args ) ] : [ 'Global', 'admin?global=1'.buildExcept( 'action', $args ) ], [ 'More items', $moreButt ], [ 'Help', 'help', 'admin' ], [ '', 'jump' ] ], [ [ 'Log out', 'logout' ] ] );
99         print "<div class='error'>$error</div>\n" if( defined $error );
100         print "<form name='admin' id='admin' class='admin' method='POST' action=''>\n";
101         my $lastId;
102         my $started = 0;
103         my $cnt = 0;
104         my $hiscnt = 0;
105         my $subcnt;
106         my $stock = loadStock();
107         print "<input type='hidden' name='jscript-active' id='jscript-active' value='0'>";#Trick to find out if user has JScript
108         print "<script type='text/javascript'><!--\n document.getElementById(\"jscript-active\").value = \"1\"; \n";
109         if( @{$stock} ) {
110                 print "function answers() {\nreturn new Array( \"\" ";
111                 foreach( @{$stock} ) {
112                         my( $x, $text ) = @{$_};
113                         $text =~ s/"/\\"/g;
114                         $text =~ s/\n/\\n/g;
115                         print ', "'.$text.'"';
116                 }
117                 print ");\n}\n";
118         }
119         print '
120 function answer( id ) {
121         var ans, index;
122         ans = answers();
123         index = document.getElementById( "ans-" + id ).value;
124         document.getElementById( "disc-" + id ).value = ans[index];
125 }
126 ';
127         print "// --></script>";
128         foreach( @{$tables->adminDump( $prefix, $limit )} ) {
129                 my( $locId, $actName, $actNote, $actHist, $actEmail, $actLogin, $actDisc, $actTime,
130                         $hist, $disc, $name, $note, $email, $login, $time ) = @{$_};
131                 if( !defined( $lastId ) || ( $lastId ne $locId ) ) {
132                         last if( $hiscnt > ( defined $limit ? $limit : 80 ) );
133                         $lastId = $locId;
134                         if( $started ) {
135                                 genNewForm( $cnt, $stock );
136                                 print "</table>\n";
137                         } else {
138                                 $started = 1;
139                         }
140                         my $addr = PciIds::Address::new( $locId );
141                         if( defined( $actHist ) ) {
142                                 print "<table class='item'>\n";
143                         } else {
144                                 print "<table class='unnamedItem'>\n";
145                         }
146                         print "<col class='author'><col class='main'><col class='controls' span='2'>\n";
147                         print "<tr class='label'><p>\n";
148                         print "<td class='path' colspan='2'>";
149                         genPathBare( $req, $addr, 1, 0 );
150                         print "<input type='hidden' name='loc-$cnt-subcnt' value='$subcnt'>" if( $subcnt );
151                         $subcnt = 0;
152                         $cnt ++;
153                         print "<td class='selects'><input type='radio' name='loc-$cnt-sel' value='curr' checked='checked'>";
154                         print "<td class='deletes'><input type='checkbox' name='loc-$cnt-del' value='del'>" if hasRight( $auth->{'accrights'}, 'prune' ) || ( !defined $actHist && !$tables->hasChildren( $addr->get() ) );
155                         print "<input type='hidden' name='loc-$cnt' value='$locId'>";
156                         if( defined $actHist ) {
157                                 genHist( 'main-history', $actEmail, $actLogin, $actTime, $actName, $actNote, $actDisc, "loc-$cnt-sel", 'seen', undef, undef );
158                         } else {
159                                 print "<tr class='main-history'><td class='empty' colspan='2'><td><input type='radio' name='loc-$cnt-sel' value='seen'>";
160                         }
161                 }
162                 $hiscnt ++;
163                 $subcnt ++;
164                 genHist( 'unseen-history', $email, $login, $time, $name, $note, $disc, "loc-$cnt-sel", $hist, "del-$hiscnt", "del-$hist" );
165                 print "<input type='hidden' name='owner-$hist' value='$locId'>";
166                 print "<input type='hidden' name='his-$cnt-$subcnt' value='$hist'>";
167         }
168         print "<input type='hidden' name='subcnt-$cnt' value='$subcnt'>\n" if( defined( $subcnt ) );
169         if( $started ) {
170                 genNewForm( $cnt, $stock );
171                 print "</table>\n";
172                 print "<p><input type='submit' name='submit' value='Submit'>\n";
173                 print "<input type='hidden' name='loc-$cnt-subcnt' value='$subcnt'>" if( $subcnt );
174                 print "<input type='hidden' name='max-cnt' value='$cnt'><input type='hidden' name='max-hiscnt' value='$hiscnt'>\n";
175         } else {
176                 print "<p>No pending items.\n";
177         }
178         print "</form>\n";
179         genHtmlTail();
180         return OK;
181 }
182
183 sub adminForm( $$$$ ) {
184         my( $req, $args, $tables, $auth ) = @_;
185         if( defined( $auth->{'authid'} ) && hasRight( $auth->{'accrights'}, 'validate' ) ) {
186                 return genNewAdminForm( $req, $args, $tables, undef, $auth );
187         } else {
188                 return notLoggedComplaint( $req, $args, $auth );
189         }
190 }
191
192 my $errors;
193
194 sub appendError( $ ) {
195         if( $errors eq '' ) {
196                 $errors = "<p>".shift;
197         } else {
198                 $errors .= "<br>".shift;
199         }
200 }
201
202 sub submitAdminForm( $$$$ ) {
203         my( $req, $args, $tables, $auth ) = @_;
204         my $authid = $auth->{'authid'};
205         if( defined( $authid ) && hasRight( $auth->{'accrights'}, 'validate' ) ) {
206                 my( %deleted, %approved );
207                 my $hasJscript = getFormValue( 'jscript-active', '0' );
208                 my $ans;
209                 $ans = loadStock() unless $hasJscript;#If there is jscript, no need to translate
210                 my $maxcnt = getFormValue( 'max-cnt', 0 );
211                 my $maxhiscnt = getFormValue( 'max-hiscnt', 0 );
212                 $errors = '';
213                 # Scan for deleted histories
214                 for( my $i = 1; $i <= $maxhiscnt; $i ++ ) {
215                         my( $del ) = getFormValue( "del-$i", '' ) =~ /^del-(\d+)$/;
216                         $deleted{$del} = 1 if( defined $del && $del ne '' );
217                 }
218                 for( my $i = 1; $i <= $maxcnt; $i ++ ) {
219                         my( $sel ) = getFormValue( "loc-$i-sel", '' ) =~ /^(\d+)$/;
220                         $approved{$sel} = 1 if( defined $sel && $sel ne '' );
221                 }
222                 # Check for collisions
223                 my %collision;
224                 foreach my $id ( keys %deleted ) {
225                         if( $approved{$id} ) {
226                                 my $owner = getFormValue( "owner-$id", '' );
227                                 appendError( "You can not approve and delete history at the same time, not modifying item ".PciIds::Address::new( $owner )->pretty() );
228                                 $collision{$owner} = $_;
229                                 delete $deleted{$id};
230                                 delete $approved{$id};
231                         }
232                 }
233                 #Do the deletes
234                 my %modified;
235                 foreach my $del ( keys %deleted ) {
236                         $tables->deleteHistory( $del );
237                         $modified{getFormValue( "owner-$del", '' )} = 1;
238                         tulog( $authid, "Discussion deleted $del" );
239                 }
240                 #Handle the items
241                 for( my $i = 1; $i <= $maxcnt; $i ++ ) {
242                         my $addr = PciIds::Address::new( getFormValue( "loc-$i", '' ) );
243                         next if $collision{$addr->get()};
244                         next unless defined $addr;
245                         my $del = getFormValue( "loc-$i-del", '' );
246                         if( defined $del && $del eq 'del' && ( hasRight( $auth->{'accrights'}, 'prune' ) || ( !$tables->hasChildren( $addr->get() ) && !$tables->hasMain( $addr->get() ) ) ) ) {
247                                 $tables->deleteItem( $addr->get() );
248                                 tulog( $authid, "Item deleted (recursive) ".$addr->get() );
249                                 next;
250                         }
251                         my $name = getFormValue( "name-$i", undef );
252                         $name = undef if defined $name && $name eq '';
253                         my $note = getFormValue( "note-$i", undef );
254                         $note = undef if defined $note && $note eq '';
255                         my $discussion = getFormValue( "disc-$i", '' );
256                         unless( $hasJscript ) {#Modified by the stock answers, is no jscript at user
257                                 my $index = getFormValue( "ans-$i", '0' );
258                                 $discussion = $ans->[$index-1]->[1] if $index;
259                         }
260                         $discussion = undef if defined $discussion && $discussion eq '';
261                         my $delete = 0;
262                         if( getFormValue( "loc-$i-softdel", '' ) =~ /^del$/ ) {
263                                 $delete = 1;
264                                 $name = undef;
265                                 $note = undef;
266                         }
267                         if( defined $note && !defined $name ) {
268                                 appendError( "You must specify name if you set note at item ".$addr->pretty() );
269                                 next;
270                         }
271                         my $action = $modified{$addr->get()};
272                         $action = 1 if getFormValue( "loc-$i-sel", '' ) eq 'seen';
273                         my( $select ) = getFormValue( "loc-$i-sel", '' ) =~ /^(\d+)$/;
274                         if( defined $name || defined $discussion || $delete ) {
275                                 my $histId = $tables->submitHistory( { 'name' => $name, 'note' => $note, 'text' => $discussion, 'delete' => $delete }, $auth, $addr );
276                                 $tables->markChecked( $histId );
277                                 $select = $histId if defined $name || $delete;
278                                 tulog( $authid, "Discussion submited (admin) $histId ".$addr->get()." ".logEscape( $name )." ".logEscape( $note )." ".logEscape( $discussion ) );
279                                 $action = 1;
280                                 notify( $tables, $addr->get(), $histId, defined $name ? 1 : 0, 1 );
281                         }
282                         if( defined $select && select ne '' ) {
283                                 $tables->setMainHistory( $addr->get(), $select );
284                                 tulog( $authid, "Item main history changed ".$addr->get()." $select" );
285                                 $action = 1;
286                                 notify( $tables, $addr->get(), $select, 2, 2 );
287                         }
288                         if( $action ) {#Approve anything in this item
289                                 my $subcnt = getFormValue( "loc-$i-subcnt", 0 );
290                                 for( my $j = 1;  $j <= $subcnt; $j ++ ) {
291                                         my( $id ) = getFormValue( "his-$i-$j", '' ) =~ /^(\d+)$/;
292                                         next unless defined $id;
293                                         next if $deleted{$id};
294                                         $tables->markChecked( $id );
295                                         tulog( $authid, "Discussion checked $id" );
296                                 }
297                         }
298                 }
299                 return genNewAdminForm( $req, $args, $tables, $errors, $auth );
300         } else {
301                 return notLoggedComplaint( $req, $args, $auth );
302         }
303 }
304
305 1;