]> ruin.nu Git - ndwebbie.git/blob - lib/NDWeb/Controller/Wiki.pm
Be more paranoid with param in list context
[ndwebbie.git] / lib / NDWeb / Controller / Wiki.pm
1 package NDWeb::Controller::Wiki;
2
3 use strict;
4 use warnings;
5 use parent 'Catalyst::Controller';
6
7 use Text::MediawikiFormat prefix => '/wiki/';
8
9 =head1 NAME
10
11 NDWeb::Controller::Wiki - Catalyst Controller
12
13 =head1 DESCRIPTION
14
15 Catalyst Controller.
16
17 =head1 METHODS
18
19 =cut
20
21
22 =head2 index
23
24 =cut
25
26 sub auto : Priate {
27         my ( $self, $c ) = @_;
28
29         $c->stash(wikiformat => \&wikiformat);
30 }
31
32 sub index :Path :Args(1) {
33         my ( $self, $c, $page ) = @_;
34
35         $c->forward('page',$page);
36         $c->stash(template => 'wiki/page.tt2');
37 }
38
39 sub main :Path :Args(0) {
40         my ( $self, $c ) = @_;
41
42         $c->forward('page', ['Info:Main']);
43         $c->stash(template => 'wiki/page.tt2');
44 }
45
46 sub page : Private {
47         my ( $self, $c, $p ) = @_;
48         my $dbh = $c->model;
49
50         $c->forward('findPage');
51         $c->acl_access_denied('test',$c->action,'No edit access for this page')
52                 if defined $c->stash->{page}->{view} && !$c->stash->{page}->{view};
53         $c->forward('loadText');
54
55         unless ($c->stash->{page}->{wpid}){
56                 $c->stash->{page}->{namespace} = $c->stash->{namespace};
57                 $c->stash->{page}->{name} = $c->stash->{name};
58                 $c->stash->{page}->{fullname} = ($c->stash->{page}->{namespace} ? $c->stash->{page}->{namespace}.':' : '')
59                         . $c->stash->{page}->{name};
60                 $c->stash->{page}->{post} = $dbh->selectrow_array(q{SELECT post
61                                 FROM wiki_namespace_access
62                                 WHERE namespace = COALESCE($1,'') AND post AND gid IN (SELECT groups($2))
63                         },undef,$c->stash->{page}->{namespace}, $c->stash->{UID});
64         }
65         $c->stash(title => $c->stash->{page}->{fullname});
66 }
67
68 sub edit :Local :Args(1) {
69         my ( $self, $c, @p ) = @_;
70         my $dbh = $c->model;
71
72         $c->forward('findPage');
73         $c->acl_access_denied('test',$c->action,'No edit access for this page')
74                 if defined $c->stash->{page}->{edit} && !$c->stash->{page}->{edit};
75         $c->forward('loadText');
76         $c->forward('findNamespaces');
77
78         unless ($c->stash->{page}->{wpid}){
79                 $c->acl_access_denied('test',$c->action,'No edit access for this page')
80                         unless @{$c->stash->{namespaces}};
81                 $c->stash->{page}->{namespace} = $c->stash->{namespace};
82                 $c->stash->{page}->{name} = $c->stash->{name};
83         }
84 }
85
86 sub history :Local :Args(1) {
87         my ( $self, $c ) = @_;
88         my $dbh = $c->model;
89
90         $c->forward('findPage');
91
92         my $query = $dbh->prepare(q{SELECT wprev,time,username,comment
93                 FROM wiki_page_revisions JOIN users u USING (uid)
94                 WHERE wpid = $1
95                 ORDER BY time DESC
96                 });
97         $query->execute($c->stash->{page}->{wpid});
98         $c->stash(revisions => $query->fetchall_arrayref({}) );
99         $c->stash(title => 'History for ' . $c->stash->{page}->{fullname});
100 }
101
102 sub postedit : Local {
103         my ( $self, $c, $p ) = @_;
104         my $dbh = $c->model;
105
106         my $ns = $c->req->param('namespace');
107         my $name = $c->req->param('name');
108         my $text = $c->req->param('text');
109         eval {
110                 $dbh->begin_work;
111
112                 my $wpid = $c->req->param('wpid');
113                 if ( $wpid eq 'new'){
114                         unless ($c->req->param('name') =~ /^([A-Z]\w*)$/){
115                                 die 'The name is not valid, start with a capital letter and only use alphanumerical characters or _ for the rest';
116                         }
117                         my $namespace = $dbh->selectrow_array(q{SELECT namespace
118                                 FROM wiki_namespace_access
119                                 WHERE namespace = $1 AND post AND gid IN (SELECT groups($2))
120                         },undef,$ns, $c->stash->{UID});
121
122                         my $query = $dbh->prepare(q{INSERT INTO wiki_pages (namespace,name) VALUES($1,$2) RETURNING wpid});
123                         $query->execute($namespace,$name);
124                         $wpid = $query->fetchrow;
125                 }
126                 $c->forward('findPage',[$wpid]);
127                 $c->acl_access_denied('test',$c->action,'No edit access for this page')
128                         if defined $c->stash->{page}->{edit} && !$c->stash->{page}->{edit};
129
130                 my $query = $dbh->prepare(q{INSERT INTO wiki_page_revisions
131                         (wpid,parent,text,comment,uid) VALUES($1,$2,$3,$4,$5)
132                         RETURNING wprev
133                         });
134                 $c->req->params->{parent}||= undef;
135                 my $parent = $c->req->param('parent');
136                 my $comment = $c->req->param('comment');
137                 $query->execute($wpid,$parent,$text
138                         ,$comment,$c->stash->{UID});
139                 my $rev = $query->fetchrow;
140                 $dbh->do(q{UPDATE wiki_pages SET wprev = $1 WHERE wpid = $2}
141                         ,undef,$rev,$wpid);
142
143                 $dbh->commit;
144                 $c->res->redirect($c->uri_for($c->stash->{page}->{fullname}));
145                 return;
146         } if ($c->req->param('cmd') eq 'Submit');
147
148         if ($@){
149                 if ($@ =~ /duplicate key value violates unique constraint "wiki_pages_namespace_key"/){
150                         $c->stash(error => "Page does already exist");
151                 }elsif ($@ =~ /value too long for type character varying\(255\)/){
152                         $c->stash(error => 'The name is too long, keep it to max 255 characters');
153                 }else{
154                         $c->stash(error => $@);
155                 }
156                 $dbh->rollback;
157         }
158
159         $c->forward('findPage') if $p;
160         $c->forward('findNamespaces');
161
162         $c->stash->{page}->{namespace} = $ns;
163         $c->stash->{page}->{name} = $name;
164
165         $c->stash(text => $text);
166         $c->stash(template => 'wiki/edit.tt2');
167 }
168
169 sub search : Local {
170         my ( $self, $c ) = @_;
171         my $dbh = $c->model;
172
173         if ($c->req->param('search')){
174                 $c->stash(search => $c->req->param('search'));
175                 my $queryfunc = 'plainto_tsquery';
176                 $queryfunc = 'to_tsquery' if $c->req->param('advsearch');
177                 my $posts = $dbh->prepare(q{SELECT wp.wpid,namespace,name
178                         ,(CASE WHEN namespace <> '' THEN namespace || ':' ELSE '' END) || name AS fullname
179                         ,ts_headline(wpr.text,}.$queryfunc.q{($2)) AS headline
180                         ,ts_rank_cd(textsearch, }.$queryfunc.q{($2),32) AS rank
181                         FROM wiki_pages wp
182                                 JOIN wiki_page_revisions wpr USING (wprev)
183                         WHERE (namespace IN (SELECT namespace FROM wiki_namespace_access WHERE gid IN (SELECT groups($1)))
184                                         OR wp.wpid IN (SELECT wpid FROM wiki_page_access WHERE uid = $1))
185                                 AND textsearch @@ }.$queryfunc.q{($2)
186                         ORDER BY rank DESC
187                 });
188                 eval {
189                         my $search = $c->req->param('search');
190                         $posts->execute($c->stash->{UID},$search);
191                         my @posts;
192                         while (my $post = $posts->fetchrow_hashref){
193                                 push @posts,$post;
194                         }
195                         $c->stash(searchresults => \@posts);
196                 };
197                 if ($@){
198                         $c->stash( searcherror => $dbh->errstr);
199                 }
200         }
201
202 }
203
204 sub findPage : Private {
205         my ( $self, $c, $p ) = @_;
206         my $dbh = $c->model;
207
208         my @arguments = ($c->stash->{UID});
209         my $where;
210         if ($p =~ /^\d+$/){
211                 $where =  q{AND wpid = $2};
212                 push @arguments, $p;
213         } elsif ($p =~ /^(?:([A-Z]\w*):)?([A-Z]\w*)$/){
214                 $where = q{AND (namespace = COALESCE($2,'') AND name = $3)};
215                 push @arguments, $1, $2;
216                 $c->stash(namespace => $1);
217                 $c->stash(name => $2);
218         } else {
219                 $c->detach('/default');
220         }
221
222         my $query = q{SELECT wpid,namespace,name,wprev
223                 ,(CASE WHEN namespace <> '' THEN namespace || ':' ELSE '' END) || name AS fullname
224                 ,bool_or(COALESCE(wpa.edit,wna.edit)) AS edit
225                 ,bool_or(wna.post) AS post
226                 ,bool_or(wpa.moderate OR wna.moderate) AS moderate
227                 ,bool_or(wpa.wpid IS NOT NULL OR wna.namespace IS NOT NULL) AS view
228                 FROM wiki_pages wp
229                         LEFT OUTER JOIN (SELECT * FROM wiki_namespace_access
230                                 WHERE gid IN (SELECT groups($1))) wna USING (namespace)
231                         LEFT OUTER JOIN (SELECT * FROM wiki_page_access
232                                 WHERE uid =  $1) wpa USING (wpid)
233                 WHERE TRUE
234         } . $where . q{ GROUP BY wpid,namespace,name,wprev};
235         $query = $dbh->prepare($query);
236         $query->execute(@arguments);
237
238         my $page = $query->fetchrow_hashref;
239         $c->stash(page => $page);
240 }
241
242 sub loadText : Private {
243         my ( $self, $c, $p ) = @_;
244         my $dbh = $c->model;
245
246         my $text = $dbh->selectrow_array(q{SELECT text
247                 FROM wiki_page_revisions WHERE wprev = $1
248                 },undef,$c->stash->{page}->{wprev});
249         $c->stash(text => $text);
250 }
251
252 sub findNamespaces : Private {
253         my ( $self, $c, $p ) = @_;
254         my $dbh = $c->model;
255
256         my $query = $dbh->prepare(q{SELECT namespace FROM wiki_namespaces
257                 WHERE namespace IN (SELECT namespace FROM wiki_namespace_access WHERE post AND gid IN (SELECT groups($1)))
258                 ORDER BY namespace
259                 });
260         $query->execute($c->stash->{UID});
261         $c->stash(namespaces => $query->fetchall_arrayref({}) );
262 }
263
264
265 =head1 AUTHOR
266
267 Michael Andreen (harv@ruin.nu)
268
269 =head1 LICENSE
270
271 GPL 2.0, or later
272
273 =cut
274
275 1;