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