]> git.decadent.org.uk Git - maypole.git/commitdiff
Maypole::Application supports Maypole::HTTPD (which needs a patch).
authorDavid Baird <cpan.zerofive@googlemail.com>
Fri, 18 Nov 2005 16:41:04 +0000 (16:41 +0000)
committerDavid Baird <cpan.zerofive@googlemail.com>
Fri, 18 Nov 2005 16:41:04 +0000 (16:41 +0000)
CGI::Maypole - split collect_output() out of send_output(), for Maypole::HTTPD. Added
status attribute to request object, but only used by start_request_hook() so far. Refactored
Mp::Model::CDBI::do_edit(), for better output in Mp-Plugin-Trace.

git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@427 48953598-375a-da11-a14b-00016c27c3ee

13 files changed:
Changes
lib/Apache/MVC.pm
lib/CGI/Maypole.pm
lib/Maypole.pm
lib/Maypole/Application.pm
lib/Maypole/Manual/Terminology.pod
lib/Maypole/Model/Base.pm
lib/Maypole/Model/CDBI.pm
t/03podcoverage.t
t/maypole.t
templates/factory/maypole.css [new file with mode: 0644]
templates/maypole.css [deleted file]
wishlist.txt

diff --git a/Changes b/Changes
index 8067f8a34c3fefe8800f47e79f2fe9159a65f3d7..e0b8a503858895dda31cd8a05edf2e464ea53d78 100644 (file)
--- a/Changes
+++ b/Changes
@@ -17,23 +17,29 @@ Incompatible API changes:
 
 API additions and enhancements:
     Maypole::Application:
 
 API additions and enhancements:
     Maypole::Application:
-       -Init flag (wishlist 14123)
+       - -Init flag (wishlist 14123)
+        - recognises Maypole::HTTPD and installs Maypole::HTTPD::Frontend
+            as its frontend
     Maypole::Headers:
        add() alias to push() (wishlist 14142)
     Maypole:
     Maypole::Headers:
        add() alias to push() (wishlist 14142)
     Maypole:
-        - session() attribute, and get_session() method (no-op)
-        - user() attribute, and get_user() method (no-op)
-        - get_session() now called during handler_guts() before authenticate()
+        - get_session() method (no-op)
+        - get_user() method (no-op)
+        - get_session() is called during handler_guts() before authenticate()
         - new preprocess_path() method added and called by parse_path(), 
                parse_path() will leave any properties set by preprocess_path() in 
             place
         - start_request_hook() added
         - new preprocess_path() method added and called by parse_path(), 
                parse_path() will leave any properties set by preprocess_path() in 
             place
         - start_request_hook() added
+        - status() attribute added (though only used by start_request_hook() 
+            so far)
         - setup() split into setup(), setup_model(), and load_model_subclass()
         - added new path processing methods for ssl and default table/action
         - added make_path() 
         - added make_uri()
      Templates:
         - Improved pager macro/include
         - setup() split into setup(), setup_model(), and load_model_subclass()
         - added new path processing methods for ssl and default table/action
         - added make_path() 
         - added make_uri()
      Templates:
         - Improved pager macro/include
+        - added the status() attribute, although it's not used in many places 
+            yet
 
 Bug fixes:
     Fix to cgi_maypole.t (bug 11346)
 
 Bug fixes:
     Fix to cgi_maypole.t (bug 11346)
index 3bb33e629a549a68644819ad526f47965b367e3c..4623b9f607dbaf3c6fb2d0f47876166ed7b9d111 100644 (file)
@@ -82,7 +82,7 @@ functionality. See L<Maypole> for these:
 
 sub get_request {
     my ($self, $r) = @_;
 
 sub get_request {
     my ($self, $r) = @_;
-    my $ar = (APACHE2) ? Apache2::Request->new($r) : Apache::Request->new($r);
+    my $ar = (APACHE2) ? Apache2::Request->new($r) : Apache::Request->instance($r);
     $self->ar($ar);
 }
 
     $self->ar($ar);
 }
 
index 9b2ee7988faaf69d7f8451d06436758219094468..b8a0a48d3d40d6351f00d4533c0ca16e2953ab59 100644 (file)
@@ -7,9 +7,9 @@ use CGI::Simple;
 use Maypole::Headers;
 use Maypole::Constants;
 
 use Maypole::Headers;
 use Maypole::Constants;
 
-our $VERSION = '2.10';
+our $VERSION = '2.11';
 
 
-__PACKAGE__->mk_accessors( qw( cgi ) );
+__PACKAGE__->mk_accessors( qw/cgi/ );
 
 =head1 NAME
 
 
 =head1 NAME
 
@@ -164,12 +164,28 @@ sub get_protocol
 
 =item send_output
 
 
 =item send_output
 
+Generates output (using C<collect_output>) and prints it. 
+
 =cut
 
 sub send_output 
 {
     my $r = shift;
 =cut
 
 sub send_output 
 {
     my $r = shift;
+    print $r->collect_output;
+}
+
+=item collect_output
+
+Gathers headers and output together into a string and returns it.
+
+Splitting this code out of C<send_output> supports L<Maypole::HTTPD::Frontend>.
 
 
+=cut
+
+sub collect_output
+{
+    my $r = shift;
+    
     # Collect HTTP headers
     my %headers = (
         -type            => $r->content_type,
     # Collect HTTP headers
     my %headers = (
         -type            => $r->content_type,
@@ -181,7 +197,7 @@ sub send_output
         $headers{"-$_"} = $r->headers_out->get($_);
     }
 
         $headers{"-$_"} = $r->headers_out->get($_);
     }
 
-    print $r->cgi->header(%headers), $r->output;
+    return $r->cgi->header(%headers) . $r->output;
 }
 
 =item get_template_root
 }
 
 =item get_template_root
index 087f2b9daae744671f8cf3af72cd4886531edab8..dbae33be3580d65f667d6e79044ef9b30ca94634 100644 (file)
@@ -199,7 +199,7 @@ __PACKAGE__->mk_classdata($_) for qw( config init_done view_object );
 __PACKAGE__->mk_accessors(
     qw( params query objects model_class template_args output path
         args action template error document_encoding content_type table
 __PACKAGE__->mk_accessors(
     qw( params query objects model_class template_args output path
         args action template error document_encoding content_type table
-        headers_in headers_out stash session user)
+        headers_in headers_out stash status)
 );
 
 __PACKAGE__->config( Maypole::Config->new() );
 );
 
 __PACKAGE__->config( Maypole::Config->new() );
@@ -243,6 +243,9 @@ enable/disable debugging.
 
 You can also set the C<debug> flag via L<Maypole::Application>.
 
 
 You can also set the C<debug> flag via L<Maypole::Application>.
 
+Some packages respond to higher debug levels, try increasing it to 2 or 3.
+
+
 =cut
 
 sub debug { 0 }      
 =cut
 
 sub debug { 0 }      
@@ -312,10 +315,6 @@ sub setup_model
         $class->load_model_subclass($subclass);
         
         $config->model->adopt($subclass) if $config->model->can("adopt");
         $class->load_model_subclass($subclass);
         
         $config->model->adopt($subclass) if $config->model->can("adopt");
-
-#      eval "use $subclass"; 
-#      die "Error loading $subclass: $@"  
-#            if $@ and $@ !~ /Can\'t locate \S+ in \@INC/;
     }
 }
 
     }
 }
 
@@ -348,7 +347,7 @@ sub load_model_subclass
         (my $filename = $subclass) =~ s!::!/!g;
         die "Loading '$subclass' failed: $@\n"
                unless $@ =~ /Can\'t locate \Q$filename\E\.pm/;
         (my $filename = $subclass) =~ s!::!/!g;
         die "Loading '$subclass' failed: $@\n"
                unless $@ =~ /Can\'t locate \Q$filename\E\.pm/;
-        warn "Did not find external module for '$subclass'\n
+        warn "No external module for '$subclass'
             if $class->debug > 1;
    }
 }
             if $class->debug > 1;
    }
 }
@@ -433,13 +432,17 @@ sub handler : method
     
     # hook useful for declining static requests e.g. images, or perhaps for 
     # sanitizing request parameters
     
     # hook useful for declining static requests e.g. images, or perhaps for 
     # sanitizing request parameters
-    my $status = $self->start_request_hook;
-    return $status unless $status == Maypole::Constants::OK();
+    $self->status(Maypole::Constants::OK());      # set the default
+    $self->__call_hook('start_request_hook');
+    return $self->status unless $self->status == Maypole::Constants::OK();
+    
+    die "status undefined after start_request_hook()" unless defined
+        $self->status;
     
     
-    $self->session($self->get_session);
-    $self->user($self->get_user);
+    $self->get_session;
+    $self->get_user;
     
     
-    $status = $self->handler_guts;
+    my $status = $self->handler_guts;
     
     # moving this here causes unit test failures - need to check why
     # before committing the move
     
     # moving this here causes unit test failures - need to check why
     # before committing the move
@@ -453,6 +456,39 @@ sub handler : method
     return $status;
 }
 
     return $status;
 }
 
+# Instead of making plugin authors use the NEXT::DISTINCT hoopla to ensure other 
+# plugins also get to call the hook, we can cycle through the application's 
+# @ISA and call them all here. Doesn't work for setup() though, because it's 
+# too ingrained in the stack. We could add a run_setup() method, but we'd break 
+# lots of existing code.
+sub __call_hook
+{
+    my ($self, $hook) = @_;
+    
+    my @plugins;
+    {
+        my $class = ref($self);
+        no strict 'refs';
+        @plugins = @{"$class\::ISA"};
+    }
+    
+    # this is either a custom method in the driver, or the method in the 1st 
+    # plugin, or the 'null' method in the frontend (i.e. inherited from 
+    # Maypole.pm) - we need to be careful to only call it once
+    my $first_hook = $self->can($hook);
+    $self->$first_hook;  
+    
+    my %seen = ( $first_hook => 1 );
+
+    # @plugins includes the frontend
+    foreach my $plugin (@plugins)
+    {
+        next unless my $plugin_hook = $plugin->can($hook);
+        next if $seen{$plugin_hook}++;
+        $self->$plugin_hook;
+    }
+}
+
 =item handler_guts
 
 This is the main request handling method and calls various methods to handle the
 =item handler_guts
 
 This is the main request handling method and calls various methods to handle the
@@ -602,23 +638,45 @@ sub parse_location
 =item start_request_hook
 
 This is called immediately after setting up the basic request. The default
 =item start_request_hook
 
 This is called immediately after setting up the basic request. The default
-method simply returns C<Maypole::Constants::OK>.
+method does nothing. 
 
 
-Any other return value causes Maypole to abort further processing of the
-request. This is useful for filtering out requests for static files, e.g.
-images, which should not be processed by Maypole or by the templating engine:
+The value of C<< $r->status >> is set to C<OK> before this hook is run. Your 
+implementation can change the status code, or leave it alone. 
+
+After this hook has run, Maypole will check the value of C<status>. For any
+value other than C<OK>, Maypole returns the C<status> immediately. 
+
+This is useful for filtering out requests for static files, e.g. images, which
+should not be processed by Maypole or by the templating engine:
 
     sub start_request_hook
     {
         my ($r) = @_;
        
 
     sub start_request_hook
     {
         my ($r) = @_;
        
-       return Maypole::Constants::DECLINED if $r->path =~ /\.jpg$/;
-       return Maypole::Constants::OK;
+        $r->status(DECLINED) if $r->path =~ /\.jpg$/;
     }
     }
+    
+Multiple plugins, and the driver, can define this hook - Maypole will call all
+of them. You should check for and probably not change any non-OK C<status>
+value:
 
 
+    package Maypole::Plugin::MyApp::SkipFavicon;
+    
+    sub start_request_hook
+    {
+        my ($r) = @_;
+        
+        # check if a previous plugin has already DECLINED this request
+        # - probably unnecessary in this example, but you get the idea
+        return unless $r->status == OK;
+        
+        # then do our stuff
+        $r->status(DECLINED) if $r->path =~ /favicon\.ico/;
+    }        
+     
 =cut
 
 =cut
 
-sub start_request_hook { Maypole::Constants::OK }
+sub start_request_hook { }
 
 =item is_applicable
 
 
 =item is_applicable
 
@@ -684,7 +742,7 @@ sub is_model_applicable
     my $action = $self->action;
     return 1 if $self->model_class->is_public($action);
     
     my $action = $self->action;
     return 1 if $self->model_class->is_public($action);
     
-    warn "The action '$action' is not applicable to the table $table"
+    warn "The action '$action' is not applicable to the table '$table'"
         if $self->debug;
     
     return 0;
         if $self->debug;
     
     return 0;
@@ -1328,3 +1386,55 @@ You may distribute this code under the same terms as Perl itself.
 =cut
 
 1;
 =cut
 
 1;
+
+__END__
+
+ =item register_cleanup($coderef)
+
+Analogous to L<Apache>'s C<register_cleanup>. If an Apache request object is
+available, this call simply redispatches there. If not, the cleanup is
+registered in the Maypole request, and executed when the request is
+C<DESTROY>ed.
+
+This method is only useful in persistent environments, where you need to ensure
+that some code runs when the request finishes, no matter how it finishes (e.g.
+after an unexpected error). 
+
+ =cut
+
+{
+    my @_cleanups;
+
+    sub register_cleanup
+    {
+        my ($self, $cleanup) = @_;
+        
+        die "register_cleanup() is an instance method, not a class method" 
+            unless ref $self;
+        die "Cleanup must be a coderef" unless ref($cleanup) eq 'CODE';
+        
+        if ($self->can('ar') && $self->ar)
+        {
+            $self->ar->register_cleanup($cleanup);
+        }
+        else
+        {
+            push @_cleanups, $cleanup;
+        }
+    }
+
+    sub DESTROY
+    {
+        my ($self) = @_;
+        
+        while (my $cleanup = shift @_cleanups)
+        {
+            eval { $cleanup->() };
+            if ($@)
+            {
+                warn "Error during request cleanup: $@";
+            }
+        }        
+    }    
+}
+    
index 6804cdead609ff0eb4e9c1ca8ff05df88dfec155..ae95bb7ee3447df5524c2925adbc7b193d4c0bdc 100644 (file)
@@ -2,6 +2,7 @@ package Maypole::Application;
 
 use strict;
 use warnings;
 
 use strict;
 use warnings;
+
 use UNIVERSAL::require;
 use Maypole;
 use Maypole::Config;
 use UNIVERSAL::require;
 use Maypole;
 use Maypole::Config;
@@ -15,6 +16,8 @@ sub import {
     
     my $frontend = 'Apache::MVC' if $ENV{MOD_PERL};
     
     
     my $frontend = 'Apache::MVC' if $ENV{MOD_PERL};
     
+    $frontend = 'Maypole::HTTPD::Frontend' if $ENV{MAYPOLE_HTTPD};
+    
     my $masonx;
     if ( grep { /^MasonX$/ } @plugins )
     {
     my $masonx;
     if ( grep { /^MasonX$/ } @plugins )
     {
@@ -30,30 +33,31 @@ sub import {
     my $autosetup=0;
     my $autoinit=0;
     my @plugin_modules;
     my $autosetup=0;
     my $autoinit=0;
     my @plugin_modules;
+
+    foreach (@plugins) 
     {
     {
-        foreach (@plugins) {
-            if    (/^\-Setup$/) { $autosetup++; }
-            elsif (/^\-Init$/)  { $autoinit++ }
-            elsif (/^\-Debug(\d*)$/) {
-                my $d = $1 || 1;
-                no strict 'refs';
-                *{"$caller\::debug"} = sub { $d };
-                warn "Debugging (level $d) enabled for $caller";
-            }
-            elsif (/^-.*$/) { warn "Unknown flag: $_" }
-            else {
-                my $plugin = "Maypole::Plugin::$_";
-                if ($plugin->require) {
-                    push @plugin_modules, "Maypole::Plugin::$_";
-                    warn "Loaded plugin: $plugin for $caller"
-                        if $caller->can('debug') && $caller->debug;
-                } else {
-                    die qq(Loading plugin "$plugin" for $caller failed: )
-                        . $UNIVERSAL::require::ERROR;
-                }
+        if    (/^\-Setup$/) { $autosetup++; }
+        elsif (/^\-Init$/)  { $autoinit++ }
+        elsif (/^\-Debug(\d*)$/) {
+            my $d = $1 || 1;
+            no strict 'refs';
+            *{"$caller\::debug"} = sub { $d };
+            warn "Debugging (level $d) enabled for $caller";
+        }
+        elsif (/^-.*$/) { warn "Unknown flag: $_" }
+        else {
+            my $plugin = "Maypole::Plugin::$_";
+            if ($plugin->require) {
+                push @plugin_modules, "Maypole::Plugin::$_";
+                warn "Loaded plugin: $plugin for $caller"
+                    if $caller->can('debug') && $caller->debug;
+            } else {
+                die qq(Loading plugin "$plugin" for $caller failed: )
+                    . $UNIVERSAL::require::ERROR;
             }
         }
     }
             }
         }
     }
+    
     no strict 'refs';
     push @{"${caller}::ISA"}, @plugin_modules, $frontend;
     $caller->config(Maypole::Config->new);
     no strict 'refs';
     push @{"${caller}::ISA"}, @plugin_modules, $frontend;
     $caller->config(Maypole::Config->new);
index d7bf7f433e315a4d9f0827328df1e18438518517..adc5dc85b64d017782b9db0d8d78d5d0d4739c0a 100644 (file)
@@ -151,7 +151,7 @@ The functionality provided by the Maypole model class is more accurately
 described as a Presentation Model (see below). In complex Maypole applications,\r
 it is good practise to separate the domain model (the 'heart' of the\r
 application) into a separate class hierarchy (see\r
 described as a Presentation Model (see below). In complex Maypole applications,\r
 it is good practise to separate the domain model (the 'heart' of the\r
 application) into a separate class hierarchy (see\r
-L<Maypole::Manual::Inheritance).\r
+L<Maypole::Manual::Inheritance>).\r
 \r
 The distinction is relatively unimportant when using Maypole in 'default' mode - \r
 i.e. using L<Maypole::Model::CDBI>, and allowing Maypole to autogenerate the \r
 \r
 The distinction is relatively unimportant when using Maypole in 'default' mode - \r
 i.e. using L<Maypole::Model::CDBI>, and allowing Maypole to autogenerate the \r
@@ -179,6 +179,8 @@ point, the convenience of dropping new methods into the 'shared' classes will be
 outweighed by the heuristic advantage of separating different layers into\r
 separate class hierarchies.\r
 \r
 outweighed by the heuristic advantage of separating different layers into\r
 separate class hierarchies.\r
 \r
+=back\r
+\r
 =head3 Presentation Model\r
 \r
 This pattern more accurately describes the role of the Maypole model.\r
 =head3 Presentation Model\r
 \r
 This pattern more accurately describes the role of the Maypole model.\r
@@ -196,8 +198,6 @@ queries the Presentation Model to retrieve these new values. In Maypole, this is
 the role of the C<vars()> method on L<Maypole::View::Base>, which transmits the\r
 new values to the templates.\r
 \r
 the role of the C<vars()> method on L<Maypole::View::Base>, which transmits the\r
 new values to the templates.\r
 \r
-=back\r
-\r
 =head1 AUTHOR\r
 \r
 David Baird, C<< <cpan@riverside-cms.co.uk> >>\r
 =head1 AUTHOR\r
 \r
 David Baird, C<< <cpan@riverside-cms.co.uk> >>\r
index 26288c29f15cd32d4d00a0dd117ba215cbc1c333..338f0e8a85449808cc5e9954a581cd5dceaa9c29 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use Maypole::Constants;
 use attributes ();
 
 use Maypole::Constants;
 use attributes ();
 
+# don't know why this is a global - drb
 our %remember;
 
 sub MODIFY_CODE_ATTRIBUTES 
 our %remember;
 
 sub MODIFY_CODE_ATTRIBUTES 
@@ -28,6 +29,7 @@ sub process {
     $r->{template} = $method;
     my $obj = $class->fetch_objects($r);
     $r->objects([$obj]) if $obj;
     $r->{template} = $method;
     my $obj = $class->fetch_objects($r);
     $r->objects([$obj]) if $obj;
+    
     $class->$method( $r, $obj, @{ $r->{args} } );
 }
 
     $class->$method( $r, $obj, @{ $r->{args} } );
 }
 
@@ -195,7 +197,7 @@ sub is_public
     
     return 1 if $attrs{Exported};
     
     
     return 1 if $attrs{Exported};
     
-    warn "$action not exported" if Maypole->debug;
+    warn "'$action' not exported";
     
     return 0;
 }
     
     return 0;
 }
index df8d6c8b210094f3bc23703ae8e0a1f8bc522248..6ae19f53fa12db0ee355dcebb6425e26b30fe256 100644 (file)
@@ -105,48 +105,70 @@ sub related_class {
  }
 
 
  }
 
 
-sub do_edit : Exported {
-    my ( $self, $r ) = @_;
-    my $h        = CGI::Untaint->new( %{ $r->{params} } );
-    my $creating = 0;
-    my ($obj) = @{ $r->objects || [] };
+sub do_edit : Exported 
+{
+    my ($self, $r, $obj) = @_;
+
+    my $config   = $r->config;
+    my $table    = $r->table;
+    
+    my $required_cols = $config->{$table}->{required_cols} || [];
+    
+    ($obj, my $fatal, my $creating) = $self->_do_update_or_create($r, $obj, $required_cols);
+    
+    # handle errors, if none, proceed to view the newly created/updated object
+    my %errors = $fatal ? (FATAL => $fatal) : $obj->cgi_update_errors;
+    
+    if (%errors) 
+    {
+        # Set it up as it was:
+        $r->template_args->{cgi_params} = $r->params;
+        $r->template_args->{errors}     = \%errors;
+
+        undef $obj if $creating;
+        $r->template("edit");
+    }
+    else 
+    {
+        $r->template("view");
+    }
+    
+    $r->objects( $obj ? [$obj] : []);
+}
+
+# drb - I've (probably temporarily) split this out from do_edit, so it's 
+# reported by Mp::P::Trace
+sub _do_update_or_create
+{
+    my ($self, $r, $obj, $required_cols) = @_;
+    
     my $fatal;
     my $fatal;
-    if ($obj) {
+    my $creating = 0;
+    my $h = CGI::Untaint->new( %{$r->params} );
+    
+    # update or create
+    if ($obj) 
+    {
         # We have something to edit
         # We have something to edit
-        eval {
-            $obj->update_from_cgi( $h =>
-                { required => $r->{config}{ $r->{table} }{required_cols} || [], }
-            );
-        };
+        eval { $obj->update_from_cgi( $h => {required => $required_cols} ) };
         $fatal = $@;
     }
         $fatal = $@;
     }
-    else {
-        eval {
-            $obj =
-                $self->create_from_cgi( $h =>
-                    { required => $r->{config}{ $r->{table} }{required_cols} || [], }
-            );
+    else 
+    {
+        eval { 
+            $obj = $self->create_from_cgi( $h => {required => $required_cols} ) 
         };
         };
-        if ($fatal = $@) {
+        
+        if ($fatal = $@) 
+        {
             warn "$fatal" if $r->debug;
         }
         $creating++;
     }
             warn "$fatal" if $r->debug;
         }
         $creating++;
     }
-    if ( my %errors = $fatal ? (FATAL => $fatal) : $obj->cgi_update_errors ) {
-
-        # Set it up as it was:
-        $r->{template_args}{cgi_params} = $r->{params};
-        $r->{template_args}{errors}     = \%errors;
-
-        undef $obj if $creating;
-        $r->template("edit");
-    }
-    else {
-        $r->{template} = "view";
-    }
-    $r->objects( $obj ? [$obj] : []);
+    
+    return $obj, $fatal, $creating;
 }
 }
-
+    
 sub delete : Exported {
     return shift->SUPER::delete(@_) if caller ne "Maypole::Model::Base";
     my ( $self, $r ) = @_;
 sub delete : Exported {
     return shift->SUPER::delete(@_) if caller ne "Maypole::Model::Base";
     my ( $self, $r ) = @_;
index d8d403de77c31bb16452f439866ccb42563db401..fa45ce2fbe681c2175cb95106ae8b3af866be0e7 100644 (file)
@@ -4,6 +4,6 @@ use strict;
 use Test::More;
 
 eval "use Test::Pod::Coverage 1.04";
 use Test::More;
 
 eval "use Test::Pod::Coverage 1.04";
-plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@;
+plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage ($@)" if $@;
 all_pod_coverage_ok({  also_private => [ qr/^[A-Z_]+$/ ], });
 
 all_pod_coverage_ok({  also_private => [ qr/^[A-Z_]+$/ ], });
 
index 4a0cd0234fec89e718f0ed2c0ad24eff1d885383..9e49186fe884a1004903131049d120900a8e4e73 100755 (executable)
@@ -32,7 +32,8 @@ my @API = qw/ config init_done view_object params query param objects model_clas
               make_uri get_template_root get_request
               parse_location send_output
              start_request_hook
               make_uri get_template_root get_request
               parse_location send_output
              start_request_hook
-             session get_session
+             get_session
+          get_user
               /;
                 
 can_ok(Maypole => @API);
               /;
                 
 can_ok(Maypole => @API);
diff --git a/templates/factory/maypole.css b/templates/factory/maypole.css
new file mode 100644 (file)
index 0000000..51d99a4
--- /dev/null
@@ -0,0 +1,376 @@
+html {
+    padding-right: 0px;
+    padding-left: 0px; 
+    padding-bottom: 0px; 
+    margin: 0px; 
+    padding-top: 0px
+}
+body {
+    font-family: sans-serif;
+    padding-right: 0px; 
+    padding-left: 0px; 
+    padding-bottom: 0px;
+    margin: 0px; padding-top: 0px;
+    background-color: #fff;
+}
+#frontpage_list {
+    position: absolute;
+    z-index: 5;
+    padding: 0px 100px 0px 0px;
+    margin:0 0.5%;     
+    margin-bottom:1em; 
+    margin-top: 1em;
+    background-color: #fff;
+}
+
+#frontpage_list a:hover {
+    background-color: #d0d8e4;
+}
+
+#frontpage_list ul {
+    list-style-type: square;
+}
+
+.content {
+    padding: 12px;
+    margin-top: 1px;  
+    margin-bottom:0px;
+    margin-left: 15px; 
+    margin-right: 15px;
+    border-color: #000000;
+    border-top: 0px;
+    border-bottom: 0px;
+    border-left: 1px;
+    border-right: 1px;
+}
+
+A { 
+    text-decoration: none; 
+    color:#225 
+}
+A:hover { 
+    text-decoration: underline; 
+    color:#222 
+}
+
+#title {
+    z-index: 6;
+    width: 100%;
+    height: 18px;
+    margin-top: 10px;
+    font-size: 90%;
+    border-bottom: 1px solid #ddf;
+    text-align: left;
+}
+
+#subtitle {
+    postion: absolute;
+    z-index: 6;
+    padding: 10px;
+    margin-top: 2em;
+    height: 18px;
+    text-align: left;
+    background-color: #fff;
+}
+
+input[type=text] {
+    height: 16px;
+    width: 136px;
+    font-family: sans-serif;
+    font-size: 11px;
+    color: #2E415A;
+    padding: 0px;
+    margin-bottom: 5px;
+}
+
+input[type=submit] {
+    height: 18px;
+    width: 60px;
+    font-family: sans-serif;
+    font-size: 11px;
+    border: 1px outset;
+    background-color: #fff;
+    padding: 0px 0px 2px 0px;
+    margin-bottom: 5px;
+}
+
+input:hover[type=submit] {
+    color: #fff;
+    background-color: #7d95b5;
+}
+
+textarea {
+    width: 136px;
+    font-family: sans-serif;
+    font-size: 11px;
+    color: #2E415A;
+    padding: 0px;
+    margin-bottom: 5px;
+}
+
+select {
+    height: 16px;
+    width: 140px;
+    font-family: sans-serif;
+    font-size: 12px;
+    color: #202020;
+    padding: 0px;
+    margin-bottom: 5px;
+}
+
+.deco1 {
+    font-size: 0px;
+    z-index:1;
+    border:0px;
+    border-style:solid;
+    border-color:#4d6d99;
+    background-color:#4d6d99;
+}
+
+.deco2 {
+    z-index:2;
+    border:0px;
+    border-style:solid;
+    border-color:#627ea5;
+    background-color:#627ea5;
+}
+
+
+.deco3 {
+    z-index:3;
+    border:0px;
+    border-style:solid;
+    border-color:#7d95b5;
+    background-color:#7d95b5;
+}
+                   
+.deco4 {
+    z-index:4;
+    border:0px;
+    border-style:solid;
+    border-color:#d0d8e4;
+    background-color:#d0d8e4;
+}
+                   
+
+table { 
+    border: 0px solid; 
+    background-color: #ffffff;
+}
+
+#matrix { width: 100%; }
+
+#matrix th {
+    background-color: #b5cadc;
+    border: 1px solid #778;
+    font: bold 12px Verdana, sans-serif;
+}
+
+#matrix #actionth {
+    width: 1px; 
+    padding: 0em 1em 0em 1em;
+}
+
+#matrix tr.alternate { background-color:#e3eaf0; }
+#matrix tr:hover { background-color: #b5cadc; }
+#matrix td { font: 12px Verdana, sans-serif; }
+
+#navlist {
+    padding: 3px 0;
+    margin-left: 0;
+    margin-top:3em;
+    border-bottom: 1px solid #778;
+    font: bold 12px Verdana, sans-serif;
+}
+
+#navlist li {
+    list-style: none;
+    margin: 0;
+    display: inline;
+}
+
+#navlist li a {
+    padding: 3px 0.5em;
+    margin-left: 3px;
+    border: 1px solid #778;
+    border-bottom: none;
+    background: #b5cadc;
+    text-decoration: none;
+}
+
+#navlist li a:link { color: #448; }
+#navlist li a:visited { color: #667; }
+
+#navlist li a:hover {
+    color: #000;
+    background: #eef;
+    border-top: 4px solid #7d95b5;
+    border-color: #227;
+}
+
+#navlist #active a {
+    background: white;
+    border-bottom: 1px solid white;
+    border-top: 4px solid;
+}
+
+td { font: 12px Verdana, sans-serif; }
+
+
+fieldset {
+    margin-top: 1em;
+    padding: 1em;
+    background-color: #f3f6f8;
+    font:80%/1 sans-serif;
+    border:1px solid #ddd;
+}
+
+legend {
+    padding: 0.2em 0.5em;
+    background-color: #fff;
+    border:1px solid #aaa;
+    font-size:90%;
+    text-align:right;
+}
+
+label {
+    display:block;
+}
+
+label .field {
+    float:left;
+    width:25%;
+    margin-right:0.5em;
+    padding-top:0.2em;
+    text-align:right;
+    font-weight:bold;
+}
+
+#vlist {
+    padding: 0 1px 1px;
+    margin-left: 0;
+    font: bold 12px Verdana, sans-serif;
+    background: gray;
+    width: 13em;
+}
+
+#vlist li {
+    list-style: none;
+    margin: 0;
+    border-top: 1px solid gray;
+    text-align: left;
+}
+
+#vlist li a {
+    display: block;
+    padding: 0.25em 0.5em 0.25em 0.75em;
+    border-left: 1em solid #7d95b5;
+    background: #d0d8e4;
+    text-decoration: none;
+}
+
+#vlist li a:hover { 
+    border-color: #227;
+}
+
+.view .field {
+    background-color: #f3f6f8;
+    border-left: 1px solid #7695b5;
+    border-top: 1px solid #7695b5;
+    padding: 1px 10px 0px 2px;
+}
+
+#addnew {
+    width: 50%;
+    float: left;
+}
+
+#search {
+    width: 50%;
+    float:right;
+}
+
+.error { color: #d00; }
+
+.action {
+    border: 1px outset #7d95b5;
+    style:block;
+}
+
+.action:hover {
+    color: #fff;
+    text-decoration: none;
+    background-color: #7d95b5;
+}
+
+.actionform {
+    display: inline;
+}
+
+.actionbutton {
+    height: 16px;
+    width: 40px;
+    font-family: sans-serif;
+    font-size: 10px;
+    border: 1px outset;
+    background-color: #fff;
+    margin-bottom: 0px;
+}
+
+.actionbutton:hover {
+    color: #fff;
+    background-color: #7d95b5;
+}
+
+.actions {
+    white-space: nowrap;
+}
+
+.field {
+    display:inline;
+}
+
+#login { width: 400px; }
+
+#login input[type=text] { width: 150px; }
+#login input[type=password] { width: 150px; }
+
+.pager {
+    font: 11px Arial, Helvetica, sans-serif;
+    text-align: center;
+    border: solid 1px #e2e2e2;
+    border-left: 0;
+    border-right: 0;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    margin: 0px;
+    background-color: #f3f6f8;
+}
+
+.pager a {
+    padding: 2px 6px;
+    border: solid 1px #ddd;
+    background: #fff;
+    text-decoration: none;
+}
+
+.pager a:visited {
+    padding: 2px 6px;
+    border: solid 1px #ddd;
+    background: #fff;
+    text-decoration: none;
+}
+
+.pager .current-page {
+    padding: 2px 6px;
+    font-weight: bold;
+    vertical-align: top;
+}
+
+.pager a:hover {
+    color: #fff;
+    background: #7d95b5;
+    border-color: #036;
+    text-decoration: none;
+}
+
diff --git a/templates/maypole.css b/templates/maypole.css
deleted file mode 100644 (file)
index 51d99a4..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-html {
-    padding-right: 0px;
-    padding-left: 0px; 
-    padding-bottom: 0px; 
-    margin: 0px; 
-    padding-top: 0px
-}
-body {
-    font-family: sans-serif;
-    padding-right: 0px; 
-    padding-left: 0px; 
-    padding-bottom: 0px;
-    margin: 0px; padding-top: 0px;
-    background-color: #fff;
-}
-#frontpage_list {
-    position: absolute;
-    z-index: 5;
-    padding: 0px 100px 0px 0px;
-    margin:0 0.5%;     
-    margin-bottom:1em; 
-    margin-top: 1em;
-    background-color: #fff;
-}
-
-#frontpage_list a:hover {
-    background-color: #d0d8e4;
-}
-
-#frontpage_list ul {
-    list-style-type: square;
-}
-
-.content {
-    padding: 12px;
-    margin-top: 1px;  
-    margin-bottom:0px;
-    margin-left: 15px; 
-    margin-right: 15px;
-    border-color: #000000;
-    border-top: 0px;
-    border-bottom: 0px;
-    border-left: 1px;
-    border-right: 1px;
-}
-
-A { 
-    text-decoration: none; 
-    color:#225 
-}
-A:hover { 
-    text-decoration: underline; 
-    color:#222 
-}
-
-#title {
-    z-index: 6;
-    width: 100%;
-    height: 18px;
-    margin-top: 10px;
-    font-size: 90%;
-    border-bottom: 1px solid #ddf;
-    text-align: left;
-}
-
-#subtitle {
-    postion: absolute;
-    z-index: 6;
-    padding: 10px;
-    margin-top: 2em;
-    height: 18px;
-    text-align: left;
-    background-color: #fff;
-}
-
-input[type=text] {
-    height: 16px;
-    width: 136px;
-    font-family: sans-serif;
-    font-size: 11px;
-    color: #2E415A;
-    padding: 0px;
-    margin-bottom: 5px;
-}
-
-input[type=submit] {
-    height: 18px;
-    width: 60px;
-    font-family: sans-serif;
-    font-size: 11px;
-    border: 1px outset;
-    background-color: #fff;
-    padding: 0px 0px 2px 0px;
-    margin-bottom: 5px;
-}
-
-input:hover[type=submit] {
-    color: #fff;
-    background-color: #7d95b5;
-}
-
-textarea {
-    width: 136px;
-    font-family: sans-serif;
-    font-size: 11px;
-    color: #2E415A;
-    padding: 0px;
-    margin-bottom: 5px;
-}
-
-select {
-    height: 16px;
-    width: 140px;
-    font-family: sans-serif;
-    font-size: 12px;
-    color: #202020;
-    padding: 0px;
-    margin-bottom: 5px;
-}
-
-.deco1 {
-    font-size: 0px;
-    z-index:1;
-    border:0px;
-    border-style:solid;
-    border-color:#4d6d99;
-    background-color:#4d6d99;
-}
-
-.deco2 {
-    z-index:2;
-    border:0px;
-    border-style:solid;
-    border-color:#627ea5;
-    background-color:#627ea5;
-}
-
-
-.deco3 {
-    z-index:3;
-    border:0px;
-    border-style:solid;
-    border-color:#7d95b5;
-    background-color:#7d95b5;
-}
-                   
-.deco4 {
-    z-index:4;
-    border:0px;
-    border-style:solid;
-    border-color:#d0d8e4;
-    background-color:#d0d8e4;
-}
-                   
-
-table { 
-    border: 0px solid; 
-    background-color: #ffffff;
-}
-
-#matrix { width: 100%; }
-
-#matrix th {
-    background-color: #b5cadc;
-    border: 1px solid #778;
-    font: bold 12px Verdana, sans-serif;
-}
-
-#matrix #actionth {
-    width: 1px; 
-    padding: 0em 1em 0em 1em;
-}
-
-#matrix tr.alternate { background-color:#e3eaf0; }
-#matrix tr:hover { background-color: #b5cadc; }
-#matrix td { font: 12px Verdana, sans-serif; }
-
-#navlist {
-    padding: 3px 0;
-    margin-left: 0;
-    margin-top:3em;
-    border-bottom: 1px solid #778;
-    font: bold 12px Verdana, sans-serif;
-}
-
-#navlist li {
-    list-style: none;
-    margin: 0;
-    display: inline;
-}
-
-#navlist li a {
-    padding: 3px 0.5em;
-    margin-left: 3px;
-    border: 1px solid #778;
-    border-bottom: none;
-    background: #b5cadc;
-    text-decoration: none;
-}
-
-#navlist li a:link { color: #448; }
-#navlist li a:visited { color: #667; }
-
-#navlist li a:hover {
-    color: #000;
-    background: #eef;
-    border-top: 4px solid #7d95b5;
-    border-color: #227;
-}
-
-#navlist #active a {
-    background: white;
-    border-bottom: 1px solid white;
-    border-top: 4px solid;
-}
-
-td { font: 12px Verdana, sans-serif; }
-
-
-fieldset {
-    margin-top: 1em;
-    padding: 1em;
-    background-color: #f3f6f8;
-    font:80%/1 sans-serif;
-    border:1px solid #ddd;
-}
-
-legend {
-    padding: 0.2em 0.5em;
-    background-color: #fff;
-    border:1px solid #aaa;
-    font-size:90%;
-    text-align:right;
-}
-
-label {
-    display:block;
-}
-
-label .field {
-    float:left;
-    width:25%;
-    margin-right:0.5em;
-    padding-top:0.2em;
-    text-align:right;
-    font-weight:bold;
-}
-
-#vlist {
-    padding: 0 1px 1px;
-    margin-left: 0;
-    font: bold 12px Verdana, sans-serif;
-    background: gray;
-    width: 13em;
-}
-
-#vlist li {
-    list-style: none;
-    margin: 0;
-    border-top: 1px solid gray;
-    text-align: left;
-}
-
-#vlist li a {
-    display: block;
-    padding: 0.25em 0.5em 0.25em 0.75em;
-    border-left: 1em solid #7d95b5;
-    background: #d0d8e4;
-    text-decoration: none;
-}
-
-#vlist li a:hover { 
-    border-color: #227;
-}
-
-.view .field {
-    background-color: #f3f6f8;
-    border-left: 1px solid #7695b5;
-    border-top: 1px solid #7695b5;
-    padding: 1px 10px 0px 2px;
-}
-
-#addnew {
-    width: 50%;
-    float: left;
-}
-
-#search {
-    width: 50%;
-    float:right;
-}
-
-.error { color: #d00; }
-
-.action {
-    border: 1px outset #7d95b5;
-    style:block;
-}
-
-.action:hover {
-    color: #fff;
-    text-decoration: none;
-    background-color: #7d95b5;
-}
-
-.actionform {
-    display: inline;
-}
-
-.actionbutton {
-    height: 16px;
-    width: 40px;
-    font-family: sans-serif;
-    font-size: 10px;
-    border: 1px outset;
-    background-color: #fff;
-    margin-bottom: 0px;
-}
-
-.actionbutton:hover {
-    color: #fff;
-    background-color: #7d95b5;
-}
-
-.actions {
-    white-space: nowrap;
-}
-
-.field {
-    display:inline;
-}
-
-#login { width: 400px; }
-
-#login input[type=text] { width: 150px; }
-#login input[type=password] { width: 150px; }
-
-.pager {
-    font: 11px Arial, Helvetica, sans-serif;
-    text-align: center;
-    border: solid 1px #e2e2e2;
-    border-left: 0;
-    border-right: 0;
-    padding-top: 10px;
-    padding-bottom: 10px;
-    margin: 0px;
-    background-color: #f3f6f8;
-}
-
-.pager a {
-    padding: 2px 6px;
-    border: solid 1px #ddd;
-    background: #fff;
-    text-decoration: none;
-}
-
-.pager a:visited {
-    padding: 2px 6px;
-    border: solid 1px #ddd;
-    background: #fff;
-    text-decoration: none;
-}
-
-.pager .current-page {
-    padding: 2px 6px;
-    font-weight: bold;
-    vertical-align: top;
-}
-
-.pager a:hover {
-    color: #fff;
-    background: #7d95b5;
-    border-color: #036;
-    text-decoration: none;
-}
-
index 7ee5bfbb76034566236441a483c9573736a0d972..1ce65b5ba7860562d03fb8e38e5472f5670441c9 100644 (file)
@@ -12,7 +12,7 @@ Fix bug 14570 - returning error codes breaks CGI::Maypole
 Write Maypole::Manual::Exceptions\r
 Test and refactor external_redirect()\r
 \r
 Write Maypole::Manual::Exceptions\r
 Test and refactor external_redirect()\r
 \r
-Fix Mp::P::USC\r
+Fix Mp::P::USC\r
 \r
 2.12\r
 ====\r
 \r
 2.12\r
 ====\r
@@ -25,7 +25,9 @@ Handle repeat form submissions.
 Implement internal_redirect().\r
 Build a more sophisticated app for testing. \r
 Move class_of() to the controller - need to do this to support multiple models. \r
 Implement internal_redirect().\r
 Build a more sophisticated app for testing. \r
 Move class_of() to the controller - need to do this to support multiple models. \r
-Multiple model support.\r
+Multiple model support - URLs like /$base/$model/$table/$action/$id.\r
+Refactor M-P-USC and M-P-Session into M-P-User, M-P-Session, and M-P-Cookie\r
+\r
 \r
 3.0\r
 ====\r
 \r
 3.0\r
 ====\r