File Coverage

lib/CGI/Mungo.pm
Criterion Covered Total %
statement 89 156 57.0
branch 17 54 31.4
condition 4 21 19.0
subroutine 19 25 76.0
pod 9 9 100.0
total 138 265 52.0


line stmt bran cond sub pod time code
1             #main framework object
2             package CGI::Mungo;
3              
4             =pod
5              
6             =head1 NAME
7              
8             CGI::Mungo - Very simple CGI web framework
9              
10             =head1 SYNOPSIS
11              
12             my $options = {
13             'responsePlugin' => 'Some::Class'
14             };
15             my $m = App->new($options);
16             $m->run(); #do this thing!
17             ###########################
18             package App;
19             use base qw(CGI::Mungo);
20             sub handleDefault{
21             #add code here for landing page
22             }
23              
24             =head1 DESCRIPTION
25              
26             All action subs are passed a L object as the only parameter, from this you should be able to reach
27             everything you need.
28              
29             =head1 METHODS
30              
31             =cut
32              
33 3     3   85401 use strict;
  3         10  
  3         112  
34 3     3   16 use warnings;
  3         7  
  3         89  
35 3     3   24 use Carp;
  3         6  
  3         265  
36 3     3   2635 use Class::Load qw(is_class_loaded);
  3         166188  
  3         207  
37 3     3   27 use base qw(CGI::Mungo::Base CGI::Mungo::Utils CGI::Mungo::Log);
  3         7  
  3         1438  
38 3     3   1419 use CGI::Mungo::Response;
  3         8  
  3         75  
39 3     3   1481 use CGI::Mungo::Session; #for session management
  3         10  
  3         105  
40 3     3   1947 use CGI::Mungo::Request;
  3         11  
  3         3083  
41             our $VERSION = "1.9";
42             #########################################################
43              
44             =head2 new(\%options)
45              
46             my $options = {
47             'responsePlugin' => 'Some::Class',
48             'checkReferer' => 0,
49             'sessionClass' => 'Some::Class',
50             'requestClass' => 'Some::Class',
51             'SefUrls' => 0,
52             'debug' => 1
53             };
54             my $m = CGI::Mungo->new($options);
55              
56             Constructor, requires a hash references to be passed as the only argument. This hash reference contains any general
57             options for the framework.
58              
59             =cut
60              
61             #########################################################
62             sub new{
63 2     2 1 1312 my($class, $options) = @_;
64 2 50       11 if($options->{'responsePlugin'}){ #this option is mandatory
65 2         27 my $self = $class->SUPER::new();
66 2         26 $self->{'_options'} = $options;
67 2         12 my $sessionClass = $self->__getFullClassName("Session");
68 2 50       10 if($self->getOption('sessionClass')){
69 0         0 $sessionClass = $self->getOption('sessionClass');
70             }
71 2         18 $self->{'_session'} = $sessionClass->new();
72 2         7 my $requestClass = $self->__getFullClassName("Request");
73 2 50       9 if($self->getOption('requestClass')){
74 0         0 $requestClass = $self->getOption('requestClass');
75             }
76 2 100       8 if(!defined($self->getOption('debug'))){ #turn off debugging by default
77 1         5 $self->_setOption("debug", 0);
78             }
79 2         24 $self->{'_request'} = $requestClass->new();
80 2         12 $self->{'_response'} = CGI::Mungo::Response->new($self, $self->getOption('responsePlugin')); #this could need access to a request object
81 2         14 $self->_init(); #perform initial setup
82 2         10 return $self;
83             }
84             else{
85 0         0 confess("No reponse plugin option provided");
86             }
87 0         0 return undef;
88             }
89             #########################################################
90              
91             =pod
92              
93             =head2 getResponse()
94              
95             my $response = $m->getResponse();
96              
97             Returns an instance of the response plugin object, previously defined in the constructor options.
98             See L for more details.
99              
100             =cut
101              
102             ###########################################################
103             sub getResponse{
104 4     4 1 831 my $self = shift;
105 4         12 return $self->{'_response'};
106             }
107             #########################################################
108              
109             =pod
110              
111             =head2 getSession()
112              
113             my $session = $m->getSession();
114              
115             Returns an instance of the L object.
116              
117             =cut
118              
119             ###########################################################
120             sub getSession{
121 3     3 1 516 my $self = shift;
122 3         10 return $self->{'_session'};
123             }
124             #########################################################
125              
126             =pod
127              
128             =head2 getRequest()
129              
130             my $request = $m->getRequest();
131              
132             Returns an instance of the L object.
133              
134             =cut
135              
136             ###########################################################
137             sub getRequest{
138 1     1 1 509 my $self = shift;
139 1         4 my $request = $self->{'_request'};
140 1 50       5 if(!$request){
141 0         0 confess("No request object found");
142             }
143 1         3 return $request;
144             }
145             #########################################################
146              
147             =pod
148              
149             =head2 getAction()
150              
151             my $action = $m->getAction();
152              
153             Returns the curent action that the web application is performing. This is the current value of the "action"
154             request form field or query string item.
155              
156             If search engine friendly URLs are turned on the action will be determined from the last part of the script URL.
157              
158             =cut
159              
160             ###########################################################
161             sub getAction{
162 0     0 1 0 my $self = shift;
163 0         0 my $action = "default";
164 0 0 0     0 if(defined($self->getOption('sefUrls')) && $self->getOption('sefUrls')){ #do we have search engine friendly urls
165 0         0 my $sefAction = $self->_getSefAction();
166 0 0       0 if($sefAction){
167 0         0 $action = $sefAction;
168             }
169             }
170             else{ #get action from query string or post string
171 0         0 my $request = $self->getRequest();
172 0         0 my $params = $request->getParameters();
173 0 0       0 if(defined($params->{'action'})){
174 0         0 $action = $params->{'action'};
175             }
176             }
177 0         0 return $action;
178             }
179             #########################################################
180              
181             =pod
182              
183             =head2 getFullUrl()
184              
185             my $url = $m->getFullUrl();
186              
187             Returns the full URL for the application.
188              
189             =cut
190              
191             #########################################################
192             sub getFullUrl{
193 1     1 1 2 my $self = shift;
194 1         3 my $url = undef;
195 1 50 33     7 if(defined($self->getOption('sefUrls')) && $self->getOption('sefUrls')){ #do we have search engine friendly urls
196 0         0 $url = $self->getSiteUrl() . "/";
197             }
198             else{
199 1         4 $url = $self->getThisUrl();
200             }
201 1         6 return $url;
202             }
203             #########################################################
204              
205             =pod
206              
207             =head2 getUrlForAction($action, $queryString)
208              
209             my $url = $m->getUrlForAction("someAction", "a=b&c=d");
210              
211             Returns the Full URL for the application with the given action and query string
212              
213             =cut
214              
215             #########################################################
216             sub getUrlForAction{
217 0     0 1 0 my($self, $action, $query) = @_;
218 0         0 my $url = undef;
219 0 0 0     0 if(defined($self->getOption('sefUrls')) && $self->getOption('sefUrls')){ #do we have search engine friendly urls
220 0         0 $url = $self->getSiteUrl() . "/";
221 0 0       0 if($query){ #add query string
222 0         0 $url .= "?" . $query;
223             }
224             }
225             else{
226 0         0 $url = $self->getThisUrl() . "?action=" . $action;
227 0 0       0 if($query){ #add query string
228 0         0 $url .= "&" . $query;
229             }
230             }
231 0         0 return $url;
232             }
233             #########################################################
234              
235             =pod
236              
237             =head2 run()
238              
239             $m->run();
240              
241             This methood is required for the web application to deal with the current request.
242             It should be called after any setup is done.
243              
244             If the response object decides that the response has not been modified then this
245             method will not run any action functions.
246              
247             The action sub run will be determined by first checking the actions hash if previously
248             given to the object then by checking if a method prefixed with "handle" exists in the
249             current class.
250              
251             =cut
252              
253             ###########################################################
254             sub run{ #run the code for the given action
255 0     0 1 0 my $self = shift;
256 0         0 my $response = $self->getResponse();
257 0 0       0 if($response->code() != 304){ #need to do something
258 0         0 $self->log("Need to run action sub");
259 0         0 my $action = $self->getAction();
260 0 0       0 if($self->getOption('debug')){
261 0         0 $self->log("Using action: '$action'");
262             }
263 0         0 my $subName = "handle" . ucfirst($action); #add prefix for security
264 0         0 my $class = ref($self);
265 0 0       0 if($class->can($subName)){ #default action sub exists
266 0         0 $self->log('Using action from auto default');
267 0         0 eval{
268 0         0 $self->$subName();
269             };
270 0 0       0 if($@){ #problem with sub
271 0         0 $response->setError("
" . $@ . "
");
272             }
273             }
274             else{ #no code to execute
275 0         0 $response->code(404);
276 0         0 $response->message('Not Found');
277 0         0 $response->setError("No action sub found for: $action");
278             }
279             }
280 0         0 $response->display(); #display the output to the browser
281 0         0 return 1;
282             }
283             ##########################################################
284              
285             =pod
286              
287             =head2 getOption("key")
288              
289             my $value = $m->getOption("debug");
290              
291             Returns the value of the configuration option given.
292              
293             =cut
294              
295             ##########################################################
296             sub getOption{
297 14     14 1 27 my($self, $key) = @_;
298 14         22 my $value = undef;
299 14 100       54 if(defined($self->{'_options'}->{$key})){ #this config option has been set
300 6         18 $value = $self->{'_options'}->{$key};
301             }
302 14         77 return $value;
303             }
304             ###########################################################
305             # Private methods
306             #########################################################
307             sub __getFullClassName{
308 4     4   11 my($self, $name) = @_;
309 3     3   22 no strict 'refs';
  3         5  
  3         2230  
310 4         7 my $class = ref($self);
311 4         7 my $baseClass = @{$class . "::ISA"}[0]; #get base classes
  4         20  
312 4         10 my $full = $baseClass . "::" . $name; #default to base class
313 4 50       29 if(is_class_loaded($class . "::" . $name)){
314 4         260 $full = $class . "::" . $name
315             }
316 4         12 return $full;
317             }
318             #########################################################
319             sub __getActionDigest{
320 0     0   0 my $self = shift;
321 0         0 my $sha1 = Digest::SHA1->new();
322 0         0 $sha1->add($self->getAction());
323 0         0 return $sha1->hexdigest();
324             }
325             ###########################################################
326             sub _getSefAction{
327 0     0   0 my $action = undef;
328 0         0 my @checkVars = ('SCRIPT_URL', 'REDIRECT_URL'); #possible places to look for actions
329 0         0 foreach my $check (@checkVars){
330 0 0 0     0 if(defined($ENV{$check}) && $ENV{$check} =~ m/\/(.+)$/){ #get the action from the last part of the url
331 0         0 $action = $1;
332 0         0 last;
333             }
334             }
335 0         0 return $action;
336             }
337             ###########################################################
338             sub _init{ #things to do when this object is created
339 2     2   5 my $self = shift;
340 2 50 33     9 if(!defined($self->getOption('checkReferer')) || $self->getOption('checkReferer')){ #check the referer by default
341 2         8 $self->_checkReferer(); #check this first
342             }
343 2         10 my $response = $self->getResponse();
344 2         10 my $session = $self->getSession();
345 2         5 my $existingSession = 0;
346             #don't care about errors below
347 2 50       13 if($session->read()){ #check for an existing session
348 0 0       0 if($session->validate()){
349 0         0 $existingSession = 1;
350 0 0       0 if($self->getOption('debug')){
351 0         0 $self->log("Existing session: " . $session->getId());
352             }
353             }
354             }
355 2 50       7 if(!$existingSession){ #start a new session
356 2 50       13 if($session->create({}, $response)){
357 2 100       9 if($self->getOption('debug')){
358 1         3 $self->log("Created new session: " . $session->getId());
359             }
360             }
361             else{
362 0         0 $response->setError($session->getError()); #now care about errors
363             }
364             }
365 2         6 return 1;
366             }
367             ###########################################################
368             sub _checkReferer{ #simple referer check for very basic security
369 2     2   5 my $self = shift;
370 2         6 my $result = 0;
371 2         7 my $host = $ENV{'HTTP_HOST'};
372 2 50 33     104 if($host && $ENV{'HTTP_REFERER'} && $ENV{'HTTP_REFERER'} =~ m/^(http|https):\/\/$host/){ #simple check here
      33        
373 2         4 $result = 1;
374             }
375             else{
376 0         0 my $response = $self->getResponse();
377 0         0 $response->setError("Details where not sent from the correct web page");
378             }
379 2         8 return $result;
380             }
381             ##########################################################
382             sub _getActions{
383 0     0   0 my $self = shift;
384 0         0 return $self->{'_actions'};
385             }
386             ###########################################################
387             sub _setOption{
388 1     1   2 my($self, $key, $value) = @_;
389 1         3 $self->{'_options'}->{$key} = $value;
390 1         2 return 1;
391             }
392             ###########################################################
393              
394             =pod
395              
396             =head1 CONFIGURATION SUMMARY
397              
398             The following list gives a summary of each Mungo
399             configuration options.
400              
401             =head3 responsePlugin
402              
403             A scalar string consisting of the response class to use.
404              
405             See L for details on how to create your own response class, or
406             a list of response classes provided in this package.
407              
408             =head3 checkReferer
409              
410             Flag to indicate if referer checking should be performed. When enabled an
411             error will raised when the referer is not present or does not contain the server's
412             hostname.
413              
414             This option is enabled by default.
415              
416             =head3 sessionClass
417              
418             A scalar string consisting of the session class to use. Useful if you want to change the way
419             session are stored.
420              
421             Defaults to ref($self)::Session
422              
423             =head3 requestClass
424              
425             A scalar string consisting of the request class to use. Useful if you want to change the way
426             requests are handled.
427              
428             Defaults to ref($self)::Request
429              
430             =head3 sefUrls
431              
432             A boolean value indicating if search engine friendly URLS are to be used. The following .htaccess rewrite rule should be
433             used:
434              
435             RewriteEngine On
436             RewriteCond %{REQUEST_FILENAME} !-f
437             RewriteRule ^(.*)$ /cgi-bin/app.cgi [L]
438              
439             =head3 debug
440              
441             A boolean value indicating if debug mode is enabled. This can then be used in output views or code to print extra debug.
442              
443             =head1 Notes
444              
445             To change the session prefix characters use the following code at the top of your script:
446              
447             $CGI::Mungo::Session::prefix = "ABC";
448            
449             To change the session file save path use the following code at the top of your script:
450              
451             $CGI::Mungo::Session::path = "/var/tmp";
452              
453             =head1 Author
454              
455             MacGyveR
456              
457             Development questions, bug reports, and patches are welcome to the above address
458              
459             =head1 Copyright
460              
461             Copyright (c) 2012 MacGyveR. All rights reserved.
462              
463             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
464              
465             =cut
466              
467             ###########################################################
468             return 1;