[Jifty-commit] r1036 - in jifty/branches/jifty-jsan: lib lib/Jifty lib/Jifty/Action/Devel lib/Jifty/View/Mason lib/Jifty/View/Static lib/Jifty/Web lib/Jifty/Web/Form plugins plugins/EditInPlace plugins/EditInPlace/lib plugins/EditInPlace/lib/Jifty plugins/EditInPlace/lib/Jifty/Plugin plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Action plugins/EditInPlace/share plugins/EditInPlace/share/web plugins/EditInPlace/share/web/templates plugins/EditInPlace/share/web/templates/__jifty share/web/static/js share/web/templates/__jifty share/web/templates/__jifty/error t t/TestApp/lib/TestApp/Action t/TestApp/t

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Thu May 11 22:41:43 EDT 2006


Author: trs
Date: Thu May 11 22:41:39 2006
New Revision: 1036

Added:
   jifty/branches/jifty-jsan/lib/Jifty/Plugin.pm
   jifty/branches/jifty-jsan/plugins/
   jifty/branches/jifty-jsan/plugins/EditInPlace/
   jifty/branches/jifty-jsan/plugins/EditInPlace/META.yml
   jifty/branches/jifty-jsan/plugins/EditInPlace/Makefile.PL
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace.pm
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Action/
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Action/FileEditor.pm
   jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Dispatcher.pm
   jifty/branches/jifty-jsan/plugins/EditInPlace/share/
   jifty/branches/jifty-jsan/plugins/EditInPlace/share/web/
   jifty/branches/jifty-jsan/plugins/EditInPlace/share/web/templates/
   jifty/branches/jifty-jsan/plugins/EditInPlace/share/web/templates/__jifty/
   jifty/branches/jifty-jsan/plugins/EditInPlace/share/web/templates/__jifty/edit_file
   jifty/branches/jifty-jsan/share/web/templates/__jifty/error/autohandler
   jifty/branches/jifty-jsan/t/TestApp/t/06-validation.t
Removed:
   jifty/branches/jifty-jsan/lib/Jifty/Action/Devel/FileEditor.pm
   jifty/branches/jifty-jsan/share/web/templates/__jifty/edit_file
Modified:
   jifty/branches/jifty-jsan/   (props changed)
   jifty/branches/jifty-jsan/AUTHORS
   jifty/branches/jifty-jsan/Changelog
   jifty/branches/jifty-jsan/MANIFEST
   jifty/branches/jifty-jsan/lib/Jifty.pm
   jifty/branches/jifty-jsan/lib/Jifty/API.pm
   jifty/branches/jifty-jsan/lib/Jifty/Action.pm
   jifty/branches/jifty-jsan/lib/Jifty/ClassLoader.pm
   jifty/branches/jifty-jsan/lib/Jifty/Config.pm
   jifty/branches/jifty-jsan/lib/Jifty/Dispatcher.pm
   jifty/branches/jifty-jsan/lib/Jifty/Handler.pm
   jifty/branches/jifty-jsan/lib/Jifty/LetMe.pm
   jifty/branches/jifty-jsan/lib/Jifty/Util.pm
   jifty/branches/jifty-jsan/lib/Jifty/View/Mason/Handler.pm
   jifty/branches/jifty-jsan/lib/Jifty/View/Static/Handler.pm
   jifty/branches/jifty-jsan/lib/Jifty/Web.pm
   jifty/branches/jifty-jsan/lib/Jifty/Web/Form/Field.pm
   jifty/branches/jifty-jsan/lib/Jifty/Web/PageRegion.pm
   jifty/branches/jifty-jsan/share/web/static/js/jifty.js
   jifty/branches/jifty-jsan/share/web/templates/__jifty/validator.xml
   jifty/branches/jifty-jsan/t/07-limit-actions.t
   jifty/branches/jifty-jsan/t/TestApp/lib/TestApp/Action/DoSomething.pm

Log:
 r10429 at zot (orig r1030):  seanmil | 2006-05-10 08:01:04 -0400
 * Small cleanup for validation warnings
 
 * Adding myself to AUTHORS
 
 
 r10430 at zot (orig r1031):  alexmv | 2006-05-10 16:09:32 -0400
  r12949 at zoq-fot-pik:  chmrr | 2006-05-10 16:08:45 -0400
   * Dispatcher fixes -- Jifty->dispatcher was a Jifty::Dispatcher,
  which never had any rules on it, so dispatching from it never did
  anything.  Remove Jifty->dispatcher in favor of
  Jifty->handler->dispatcher, which does work.  This means that
  fragments *actually* go through the siaptcher now.
   * The main dispatcher array is RULES_RUN not RULES.
 
 r10431 at zot (orig r1032):  seanmil | 2006-05-10 17:25:07 -0400
  r14 at pc102:  sean | 2006-05-10 17:20:33 -0400
  * Added AJAXified canonicalizations
  
 
 r10433 at zot (orig r1034):  alexmv | 2006-05-11 15:11:18 -0400
  r12958 at zoq-fot-pik:  chmrr | 2006-05-11 15:07:39 -0400
   * First pass at a plugin archetecture
  
   * This also include some reworking of the ClassLoader, including
     removing the ActionBaseClass and CurrentUserPath config settings,
     which breaks backwards compatibility!  If you were using these,
     please let us know your use case.
  
 
 r10434 at zot (orig r1035):  alexmv | 2006-05-11 15:12:09 -0400
 


Modified: jifty/branches/jifty-jsan/AUTHORS
==============================================================================
--- jifty/branches/jifty-jsan/AUTHORS	(original)
+++ jifty/branches/jifty-jsan/AUTHORS	Thu May 11 22:41:39 2006
@@ -7,3 +7,4 @@
 Audrey Tang <audreyt at audreyt.org>
 Eric Wilhelm <ewilhelm at cpan.org>
 Nelson Elhage <nelhage at mit.edu>
+Sean E Millichamp <sean at enertronllc.com>

Modified: jifty/branches/jifty-jsan/Changelog
==============================================================================
--- jifty/branches/jifty-jsan/Changelog	(original)
+++ jifty/branches/jifty-jsan/Changelog	Thu May 11 22:41:39 2006
@@ -1,20 +1,25 @@
-0.60507 (7 May 2006)
+  * Removed the ActionBasePath and CurrentUserClass configuration
+    variables.  This breaks backwards compatibility.
 
-* Pod fixes from Eric Wilhelm
-  lib/Jifty/Object.pm - removed incorrect '=for' directive
-  lib/Jifty/Web/Form/Field.pm - removed incorrect '=for' directive
-  lib/Jifty/Web/Form.pm - removed incorrect '=for' directive
-  
-
-* Better failure messages on when schema upgrades with SQLite fail from Alex Vandiver
-   * Be a little more explicit about SQLite's limitation, and a possible
-     (painful) workaround
- 
-* Update the inc tree to 0.62 for various fixes,
-   in particular improved share_dir compatibility. --Audrey Tang
+0.60507 (7 May 2006)
 
-* We were inconsistently using Jifty::Util::make_path as a subroutine. It's a class method.
-  This could break the stub generators and tutorial.  Thanks to Sean E. Millichamp
+  * Pod fixes from Eric Wilhelm
+    lib/Jifty/Object.pm - removed incorrect '=for' directive
+    lib/Jifty/Web/Form/Field.pm - removed incorrect '=for' directive
+    lib/Jifty/Web/Form.pm - removed incorrect '=for' directive
+
+  * Better failure messages on when schema upgrades with SQLite fail
+    from Alex Vandiver
+
+  * Be a little more explicit about SQLite's limitation, and a possible
+    (painful) workaround
+
+  * Update the inc tree to 0.62 for various fixes,
+    in particular improved share_dir compatibility. --Audrey Tang
+
+  * We were inconsistently using Jifty::Util::make_path as a
+    subroutine. It's a class method.  This could break the stub
+    generators and tutorial.  Thanks to Sean E. Millichamp
 
 0.60505 - Cinco de Jifty! (5 May 2006)
 

Modified: jifty/branches/jifty-jsan/MANIFEST
==============================================================================
--- jifty/branches/jifty-jsan/MANIFEST	(original)
+++ jifty/branches/jifty-jsan/MANIFEST	Thu May 11 22:41:39 2006
@@ -44,7 +44,6 @@
 lib/Jifty.pm
 lib/Jifty/Action.pm
 lib/Jifty/Action/Autocomplete.pm
-lib/Jifty/Action/Devel/FileEditor.pm
 lib/Jifty/Action/Record.pm
 lib/Jifty/Action/Record/Create.pm
 lib/Jifty/Action/Record/Delete.pm
@@ -200,7 +199,6 @@
 share/web/templates/__jifty/admin/index.html
 share/web/templates/__jifty/admin/model/dhandler
 share/web/templates/__jifty/autocomplete.xml
-share/web/templates/__jifty/edit_file
 share/web/templates/__jifty/error/_elements/error_text
 share/web/templates/__jifty/error/_elements/wrapper
 share/web/templates/__jifty/error/dhandler

Modified: jifty/branches/jifty-jsan/lib/Jifty.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty.pm	Thu May 11 22:41:39 2006
@@ -62,7 +62,7 @@
 use base qw/Jifty::Object/;
 use Jifty::Everything;
 
-use vars qw/$HANDLE $CONFIG $LOGGER $HANDLER $DISPATCHER $API/;
+use vars qw/$HANDLE $CONFIG $LOGGER $HANDLER $API @PLUGINS/;
 
 =head1 METHODS
 
@@ -116,10 +116,19 @@
     push @Jifty::Record::ISA, Jifty->config->framework('Database')->{'RecordBaseClass'};
 
     __PACKAGE__->logger( Jifty::Logger->new( $args{'logger_component'} ) );
-   # Get a classloader set up
-   Jifty::ClassLoader->new->require;
+    # Get a classloader set up
+    Jifty::ClassLoader->new(base => Jifty->config->framework('ApplicationClass'))->require;
+
+    # Set up plugins
+    my @plugins;
+    for my $plugin (@{Jifty->config->framework('Plugins')}) {
+        my $class = "Jifty::Plugin::".(keys %{$plugin})[0];
+        my %options = %{ $plugin->{(keys %{$plugin})[0]} };
+        Jifty::Util->require($class);
+        push @plugins, $class->new(%options);
+    }
+    __PACKAGE__->plugins(@plugins);
 
-    __PACKAGE__->dispatcher(Jifty::Dispatcher->new());
     __PACKAGE__->handler(Jifty::Handler->new());
     __PACKAGE__->api(Jifty::API->new());
 
@@ -181,19 +190,6 @@
     return $HANDLE;
 }
 
-=head2 dispatcher
-
-An accessor for the C<Jifty::Dispatcher> object that we use to make
-decisions about how to dispatch each request made by a web client.
-
-=cut
-
-sub dispatcher {
-    my $class = shift;
-    $DISPATCHER = shift if (@_);
-    return $DISPATCHER;
-}
-
 =head2 api
 
 An accessor for the L<Jifty::API> object that publishes and controls
@@ -218,6 +214,17 @@
     return $HTML::Mason::Commands::JiftyWeb;
 }
 
+=head2 plugins
+
+Returns a list of L<Jifty::Plugin> objects for this Jifty application.
+
+=cut
+
+sub plugins {
+    my $class = shift;
+    @PLUGINS = @_ if @_;
+    return @PLUGINS;
+}
 
 =head2 setup_database_connection
 

Modified: jifty/branches/jifty-jsan/lib/Jifty/API.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/API.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/API.pm	Thu May 11 22:41:39 2006
@@ -33,9 +33,9 @@
 
     Module::Pluggable->import(
         search_path => [
-            Jifty->config->framework('ActionBasePath'),
             Jifty->config->framework('ApplicationClass') . "::Action",
-            "Jifty::Action"
+            "Jifty::Action",
+            map {ref($_)."::Action"} Jifty->plugins,
         ],
         sub_name => "_actions",
     );
@@ -46,9 +46,9 @@
 =head2 qualify ACTIONNAME
 
 Returns the fully qualified package name for the given provided
-action.  If the C<ACTIONNAME> starts with C<Jifty::Action> or your
-application's C<ActionBasePath>, simply returns the given name;
-otherwise, it prefixes it with the C<ActionBasePath>.
+action.  If the C<ACTIONNAME> starts with C<Jifty::> or
+C<ApplicationClass>::Action, simply returns the given name; otherwise,
+it prefixes it with the C<ApplicationClass>::Action.
 
 =cut
 
@@ -56,10 +56,10 @@
     my $self   = shift;
     my $action = shift;
 
-    my $base_path = Jifty->config->framework('ActionBasePath');
+    my $base_path = Jifty->config->framework('ApplicationClass') . "::Action";
 
     return $action
-        if $action =~ /^Jifty::Action/
+        if $action =~ /^Jifty::/
         or $action =~ /^\Q$base_path\E/;
 
     return $base_path . "::" . $action;
@@ -67,11 +67,10 @@
 
 =head2 reset
 
-Resets which actions are allowed to the defaults; that is, all actions
-from the application's C<ActionBasePath>, and
-L<Jifty::Action::Autocomplete> and L<Jifty::Action::Redirect> are
-allowed; everything else is denied.  See L</restrict> for the details
-of how limits are processed.
+Resets which actions are allowed to the defaults; that is, all of the
+application's actions, L<Jifty::Action::Autocomplete>, and
+L<Jifty::Action::Redirect> are allowed; everything else is denied.
+See L</restrict> for the details of how limits are processed.
 
 =cut
 
@@ -79,7 +78,7 @@
     my $self = shift;
 
     # Set up defaults
-    my $app_actions = Jifty->config->framework('ActionBasePath');
+    my $app_actions = Jifty->config->framework('ApplicationClass') . "::Action";
 
     $self->action_limits(
         [   { deny => 1, restriction => qr/.*/ },
@@ -176,10 +175,9 @@
 
 =head2 is_allowed CLASS
 
-Returns false if the I<CLASS> name (which is fully qualified with the
-application's ActionBasePath if it is not already) is allowed to be
-executed.  See L</restrict> above for the rules that the class
-name must pass.
+Returns false if the I<CLASS> name (which is fully qualified if it is
+not already) is allowed to be executed.  See L</restrict> above for
+the rules that the class name must pass.
 
 =cut
 
@@ -215,8 +213,7 @@
 
 Lists the class names of all of the allowed actions for this Jifty
 application; this may include actions under the C<Jifty::Action::>
-namespace, in addition to actions under your application's
-C<ActionBasePath>.
+namespace, in addition to your application's actions.
 
 =cut
 

Modified: jifty/branches/jifty-jsan/lib/Jifty/Action.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Action.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Action.pm	Thu May 11 22:41:39 2006
@@ -129,6 +129,20 @@
 See L<Jifty::Web::Form::Field> for the list of possible keys that each
 argument can have.
 
+In addition to the list there, you may use this additional key:
+
+=over
+
+=item ajax_canonicalizes
+
+This key takes a boolean value that determines if the value displayed in
+the form field is updated via AJAX with the result returned by this argument's
+L<canonicalize|Jifty::Manual::Glossary/canonicalize> function.
+
+Defaults to false.
+
+=back
+
 =cut
 
 sub arguments {
@@ -870,6 +884,7 @@
     my $field = shift;
 
     $self->result->field_error($field => undef);
+    $self->result->field_warning($field => undef);
 
     return 1;
 }

Modified: jifty/branches/jifty-jsan/lib/Jifty/ClassLoader.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/ClassLoader.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/ClassLoader.pm	Thu May 11 22:41:39 2006
@@ -19,11 +19,15 @@
 C<@INC> that allows L<Jifty::ClassLoader> to dynamically create needed
 classes if they do not exist already.
 
+Takes one mandatory argument, C<base>, which should be the the
+application's base path; all of the classes under this will be
+automatically loaded.
+
 =cut
 
 sub new {
     my $class = shift;
-    my $self = bless {}, $class;
+    my $self = bless {@_}, $class;
 
     push @INC, $self;
     return $self;
@@ -50,11 +54,11 @@
 
 An empty class that descends from L<Jifty::Collection> is created.
 
-=item I<ApplicationClass::Notification>.
+=item I<Application>::Notification
 
 An empty class that descends from L<Jifty::Notification>.
 
-=item I<ApplicationClass::Dispatcher>.
+=item I<Application>::Dispatcher
 
 An empty class that descends from L<Jifty::Dispatcher>.
 
@@ -62,11 +66,9 @@
 
 An empty class that descends from L<Jifty::Bootstrap>.
 
-=item I<CurrentUserClass> (generally I<Application>::CurrentUser)
+=item I<Application>::CurrentUser
 
-...where I<CurrentUserClass> is defined by the C<CurrentUserClass>
-from the L<configuration file|Jifty::Config>.  This defaults to an
-empty class which is a subclass of L<Jifty::CurrentUser>.
+An empty class that descends from L<Jifty::CurrentUser>.
 
 =item I<Application>::Model::I<Anything>Collection
 
@@ -88,61 +90,54 @@
 # This subroutine's name is fully qualified, as perl will ignore a 'sub INC'
 sub Jifty::ClassLoader::INC {
     my ( $self, $module ) = @_;
-    my $ApplicationClassPrefix = Jifty->config->framework('ApplicationClass');
-    my $ActionBasePath   = Jifty->config->framework('ActionBasePath');
-    my $CurrentUserClass = Jifty->config->framework('CurrentUserClass');
-    my $CurrentUserClassPath =Jifty->config->framework('CurrentUserClass') .".pm";
-    $CurrentUserClassPath =~ s!::!/!g;
-    return undef unless ( $module and $ApplicationClassPrefix );
 
+    my $base = $self->{base};
+    return undef unless ( $module and $base );
 
     # Canonicalize $module to :: style rather than / and .pm style;
-    
     $module =~ s/.pm$//;
     $module =~ s{/}{::}g;
 
-    if ( $module =~ m!^($ApplicationClassPrefix)$! ) {
-        return $self->return_class( "use warnings; use strict; package " . $ApplicationClassPrefix . ";\n"." 1;" );
-    } 
-#    elsif ( $module =~ m!^($ActionBasePath)$! ) {
-#        return $self->return_class( "use warnings; use strict; package " . $ActionBasePath . ";\n".
-#            "use base qw/Jifty::Action/; sub _autogenerated { 1 };\n"."1;" );
-#    } 
-    elsif ( $module =~ m!^(?:$ApplicationClassPrefix)::(Record|Collection|Notification|Dispatcher|Bootstrap)$! ) {
-        return $self->return_class( "use warnings; use strict; package " . $ApplicationClassPrefix . "::". $1.";\n".
-            "use base qw/Jifty::$1/; sub _autogenerated { 1 };\n"."1;" );
-    } 
-
-    elsif ( $module =~ m!^$CurrentUserClass$! or $module =~ m!^$CurrentUserClassPath$!) {
-      return $self->return_class( "package " . $CurrentUserClass . ";\n" . "use base 'Jifty::CurrentUser';\n" . " 1;" );
-      }
-    elsif ( $module
-        =~ m!^($ApplicationClassPrefix)::Model::(\w+)Collection$!
-        )
-    {
-
-        # Auto-create Collection classes
-        my $record_class = $ApplicationClassPrefix . "::Model::" . $2;
-        return $self->return_class( "package " . $ApplicationClassPrefix . "::Model::" . $2 . "Collection;\n"."use base qw/@{[$ApplicationClassPrefix]}::Collection/;\n sub record_class { '@{[$ApplicationClassPrefix]}::Model::$2' }\n"." 1;"
-        );
-
-    } elsif ( $module
-        =~ m!^($ApplicationClassPrefix)::Action::(Create|Update|Delete)([^\.]+)$!
-        )
-    {
-         
-        # Auto-create CRUD classes - this applies to model subclasses too
-        my $modelclass = $ApplicationClassPrefix . "::Model::" . $3;
+    # The quick check
+    return undef unless $module =~ m!^$base!;
+
+    if ( $module =~ m!^(?:$base)$! ) {
+        return $self->return_class(
+            "use warnings; use strict; package " . $base . ";\n" . " 1;" );
+    }
+#    elsif ( $module =~ m!^(?:$base)::Action$! ) {
+#        return $self->return_class(
+#                  "use warnings; use strict; package $module;\n"
+#                . "use base qw/Jifty::Action/; sub _autogenerated { 1 };\n"
+#                . "1;" );
+#    }
+    elsif ( $module =~ m!^(?:$base)::(Record|Collection|Notification|Dispatcher|Bootstrap)$! ) {
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/Jifty::$1/; sub _autogenerated { 1 };\n"
+                . "1;" );
+    } elsif ( $module =~ m!^(?:$base)::CurrentUser$! ) {
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/Jifty::CurrentUser/; sub _autogenerated { 1 };\n"
+                . "1;" );
+    } elsif ( $module =~ m!^(?:$base)::Model::(\w+)Collection$! ) {
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/@{[$base]}::Collection/;\n"
+                . "sub record_class { '@{[$base]}::Model::$1' }\n"
+                . "1;" );
+    } elsif ( $module =~ m!^(?:$base)::Action::(Create|Update|Delete)([^\.]+)$! ) {
+        my $modelclass = $base . "::Model::" . $2;
         Jifty::Util->require($modelclass);
 
-        return undef unless eval {$modelclass->table}; #self->{models}{$modelclass};
+        return undef unless eval { $modelclass->table };
 
-        my $class = $ActionBasePath ."::".$2.$3;
-        return $self->return_class( "package $class;\n"
-                . "use base qw/Jifty::Action::Record::$2/;\n"
-                . "sub record_class {'$modelclass'};\n"
+        return $self->return_class(
+                  "use warnings; use strict; package $module;\n"
+                . "use base qw/Jifty::Action::Record::$1/;\n"
+                . "sub record_class { '$modelclass' };\n"
                 . "1;" );
-
     }
     return undef;
 }
@@ -154,7 +149,6 @@
 
 =cut
 
-
 sub return_class {
     my $self = shift;
     my $content = shift;
@@ -175,28 +169,25 @@
 sub require {
     my $self = shift;
     
-    my $ApplicationClassPrefix = Jifty->config->framework('ApplicationClass');
+    my $base = $self->{base};
     # if we don't even have an application class, this trick will not work
-    return unless  ($ApplicationClassPrefix); 
-    Jifty::Util->require($ApplicationClassPrefix);
-    Jifty::Util->require(Jifty->config->framework('CurrentUserClass'));
-
-    my $ActionBasePath = Jifty->config->framework('ActionBasePath');
+    return unless ($base); 
+    Jifty::Util->require($base);
+    Jifty::Util->require($base."::CurrentUser");
 
     Module::Pluggable->import(
         search_path =>
-          [ $ActionBasePath, map { $ApplicationClassPrefix . "::" . $_ } 'Model', 'Action', 'Notification' ],
+          [ map { $base . "::" . $_ } 'Model', 'Action', 'Notification' ],
         require => 1,
         inner => 0
     );
-    $self->{models} = {map {($_ => 1)} grep {/^($ApplicationClassPrefix)::Model::(.*)$/ and not /Collection$/} $self->plugins};
+    $self->{models}{$_} = 1 for grep {/^($base)::Model::(.*)$/ and not /Collection$/} $self->plugins;
     for my $full (keys %{$self->{models}}) {
         my($short) = $full =~ /::Model::(.*)/;
         Jifty::Util->require($full . "Collection");
-        Jifty::Util->require($ActionBasePath . "::" . $_ . $short)
+        Jifty::Util->require($base . "::Action::" . $_ . $short)
             for qw/Create Update Delete/;
     }
-
 }
 
 1;

Modified: jifty/branches/jifty-jsan/lib/Jifty/Config.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Config.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Config.pm	Thu May 11 22:41:39 2006
@@ -213,9 +213,7 @@
         framework => {
             AdminMode        => 1,
             DevelMode        => 1,
-            ActionBasePath   => $app_class . "::Action",
             ApplicationClass => $app_class,
-            CurrentUserClass => $app_class . "::CurrentUser",
             ApplicationName  => $app_name,
             LogLevel         => 'INFO',
             Database         => {
@@ -232,6 +230,7 @@
             L10N       => {
                 PoDir => "%share/po%",
             },
+            Plugins    => [],
             Web        => {
                 Port => '8888',
                 BaseURL => 'http://localhost',

Modified: jifty/branches/jifty-jsan/lib/Jifty/Dispatcher.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Dispatcher.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Dispatcher.pm	Thu May 11 22:41:39 2006
@@ -284,7 +284,7 @@
 
     no strict 'refs';
     no warnings 'once';
-    for (qw(RULES RULES_SETUP RULES_CLEANUP)) {
+    for (qw(RULES_RUN RULES_SETUP RULES_CLEANUP)) {
         @{ $pkg . '::' . $_ } = ();
     }
     if ( @args != @_ ) {
@@ -1015,4 +1015,22 @@
 }
 
 
+=head2 import_plugins
+
+Imports rules from L<Jifty/plugins> into the main dispatcher's space.
+
+=cut
+
+sub import_plugins {
+    my $self = shift;
+    for my $stage (qw/SETUP RUN CLEANUP/) {
+        my @rules;
+        push @rules, $_->dispatcher->rules($stage) for Jifty->plugins;
+        push @rules, $self->rules($stage);
+
+        no strict 'refs';
+        @{ $self . "::RULES_$stage" } = @rules;
+    }
+}
+
 1;

Modified: jifty/branches/jifty-jsan/lib/Jifty/Handler.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Handler.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Handler.pm	Thu May 11 22:41:39 2006
@@ -52,6 +52,8 @@
     $self->dispatcher(
         Jifty->config->framework('ApplicationClass') . "::Dispatcher" );
     Jifty::Util->require( $self->dispatcher );
+    $self->dispatcher->import_plugins;
+
     $self->mason( Jifty::View::Mason::Handler->new( $self->mason_config ) );
 
     $self->static_handler(Jifty::View::Static::Handler->new());
@@ -108,14 +110,17 @@
         %{ Jifty->config->framework('Web')->{'MasonConfig'} },
     );
 
+    for my $plugin (Jifty->plugins) {
+        my $comp_root = $plugin->template_root;
+        next unless $comp_root;
+        push @{ $config{comp_root} }, [ ref($plugin)."-".Jifty->web->serial => $comp_root ];
+    }
+
     # In developer mode, we want halos, refreshing and all that other good stuff. 
     if (Jifty->config->framework('DevelMode') ) {
         push @{$config{'plugins'}}, 'Jifty::Mason::Halo';
-            $config{static_source}        = 0;
-            $config{use_object_files}        = 0;
-            
-            
-
+        $config{static_source}    = 0;
+        $config{use_object_files} = 0;
     }
     return (%config);
         
@@ -167,6 +172,7 @@
     Jifty->web->setup_session;
     Jifty->web->session->set_cookie;
     Jifty->api->reset;
+    $_->new_request for Jifty->plugins;
 
     Jifty->log->debug( "Received request for " . Jifty->web->request->path );
 

Modified: jifty/branches/jifty-jsan/lib/Jifty/LetMe.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/LetMe.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/LetMe.pm	Thu May 11 22:41:39 2006
@@ -72,7 +72,7 @@
 sub _user_from_email {
     my $self = shift;
     my $email = shift;
-    my $currentuser_object_class = Jifty->config->framework('CurrentUserClass');
+    my $currentuser_object_class = Jifty->config->framework('ApplicationClass')."::CurrentUser";
     return $currentuser_object_class->new( email => $email );
 }
 

Added: jifty/branches/jifty-jsan/lib/Jifty/Plugin.pm
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/lib/Jifty/Plugin.pm	Thu May 11 22:41:39 2006
@@ -0,0 +1,110 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin;
+
+=head1 NAME
+
+Jifty::Plugin - Describes a plugin to the Jifty framework
+
+=head1 DESCRIPTION
+
+Plugins are like mini-apps.  They come in packages with share
+directories which provide static and template files; they provide
+actions; they have dispatcher rules.  
+
+=cut
+
+use File::ShareDir;
+
+=head2 new
+
+Sets up a new instance of this plugin.  This is called by L<Jifty>
+after reading the configuration file, and is supplied whatever
+plugin-specific settings were in the config file.  Note that because
+plugins affect Mason's component roots, adding plugins during runtime
+is not supported.
+
+=cut
+
+sub new {
+    my $class = shift;
+    
+    # Get a classloader set up
+    Jifty::ClassLoader->new(base => $class)->require;
+    Jifty::Util->require($class->dispatcher);
+
+    # XXX TODO: Add .po path
+
+    my $self = bless {} => $class;
+    $self->init(@_);
+    return $self;
+}
+
+
+=head2 init [ARGS]
+
+Called by L</new>, this does any custom configuration that the plugin
+might need.  It is passed the same parameters as L</new>, gleaned from
+the configuration file.
+
+=cut
+
+sub init {
+    1;
+}
+
+=head2 new_request
+
+Called right before every request.  By default, this adds the plugin's
+actions to the list of allowed actions, using L<Jifty::API/allow>.
+
+=cut
+
+sub new_request {
+    my $self = shift;
+    my $class = ref($self) || $self;
+    Jifty->api->allow(qr/^\Q$class\E::Action/);
+}
+
+=head2 template_root
+
+Returns the root of the template directory for this plugin
+
+=cut
+
+sub template_root {
+    my $self = shift;
+    my $class = ref($self) || $self;
+    my $share = File::ShareDir::module_dir($class);
+    return unless $share;
+    return "$share/web/templates";
+}
+
+=head2 static_root
+
+Returns the root of the static directory for this plugin
+
+=cut
+
+sub static_root {
+    my $self = shift;
+    my $class = ref($self) || $self;
+    my $share = File::ShareDir::module_dir($class);
+    return unless $share;
+    return "$share/web/static";
+}
+
+=head2 dispatcher
+
+Returns the classname of the dispatcher class for this plugin
+
+=cut
+
+sub dispatcher {
+    my $self = shift;
+    my $class = ref($self) || $self;
+    return $class."::Dispatcher";
+}
+
+1;

Modified: jifty/branches/jifty-jsan/lib/Jifty/Util.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Util.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Util.pm	Thu May 11 22:41:39 2006
@@ -180,7 +180,7 @@
 
     my $retval = $class->require;
     if ($UNIVERSAL::require::ERROR) {
-       my $error = $UNIVERSAL::require::ERROR;
+        my $error = $UNIVERSAL::require::ERROR;
         $error =~ s/ at .*?\n$//;
         Jifty->log->error(sprintf("$error at %s line %d\n", (caller)[1,2]));
         return 0;

Modified: jifty/branches/jifty-jsan/lib/Jifty/View/Mason/Handler.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/View/Mason/Handler.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/View/Mason/Handler.pm	Thu May 11 22:41:39 2006
@@ -45,7 +45,7 @@
 
 Takes a number of key-value parameters; see L<HTML::Mason::Params>.
 Defaults the C<out_method> to L</out_method>, and the C<request_class>
-to L<HTML::MAson::request::Jifty> (below).  Finally, adds C<h> and
+to L<HTML::Mason::Request::Jifty> (below).  Finally, adds C<h> and
 C<u> escapes, which map to L</escape_uri> and L<escape_utf8>
 respectively.
 

Modified: jifty/branches/jifty-jsan/lib/Jifty/View/Static/Handler.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/View/Static/Handler.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/View/Static/Handler.pm	Thu May 11 22:41:39 2006
@@ -110,18 +110,15 @@
 sub file_path {
     my $self    = shift;
     my $file    = shift;
-    my @options = (qw(StaticRoot DefaultStaticRoot));
+    my @options = map {Jifty->config->framework('Web')->{$_}} (qw(StaticRoot DefaultStaticRoot));
+    push @options, grep {$_} map {$_->static_root} Jifty->plugins;
 
     # Chomp a leading "/static" - should this be configurable?
     $file =~ s/^\/*?static//; 
 
     foreach my $path (@options) {
-
-        my $abspath = Jifty::Util->absolute_path(
-            Jifty->config->framework('Web')->{$path} . "/" . $file );
-
+        my $abspath = Jifty::Util->absolute_path( $path . "/" . $file );
         return $abspath if ( -f $abspath && -r $abspath );
-
     }
     return undef;
 

Modified: jifty/branches/jifty-jsan/lib/Jifty/Web.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Web.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Web.pm	Thu May 11 22:41:39 2006
@@ -146,11 +146,9 @@
 If a temporary_current_user has been set, will return that instead.
 
 If the current application has no loaded current user, we get an empty
-app-specific C<CurrentUser> object. (This is determined by
-theC<framework> configuration varialbe C<CurrentUserClass> and
-defaults to $AppNameC<::CurrentUser>, a subclass of
-L<Jifty::CurrentUser>.  $AppNameC<::CurrentUser> is autogenerated if
-it doesn't exist.
+app-specific C<CurrentUser> object. (This
+C<ApplicationClass>::CurrentUser class, a subclass of
+L<Jifty::CurrentUser>, is autogenerated if it doesn't exist).
 
 =cut
 
@@ -166,7 +164,7 @@
         return $self->session->get('user');
     }
     else {
-        my $object = Jifty->config->framework('CurrentUserClass')->new();
+        my $object = (Jifty->config->framework('ApplicationClass')."::CurrentUser")->new();
         $object->is_superuser(1) if Jifty->config->framework('AdminMode');
         return ($object);
     }
@@ -319,10 +317,9 @@
 
 Creates a new action (an instance of a subclass of L<Jifty::Action>)
 
-C<CLASS> is appended to the C<ActionBasePath> found in the
-configuration file, and an instance of that class is created, passing
-the C<Jifty::Web> object, the C<MONIKER>, and any other arguments that
-C<new_action> was supplied.
+C<CLASS> is L<qualified|Jifty::Util/qualify>, and an instance of that
+class is created, passing the C<Jifty::Web> object, the C<MONIKER>,
+and any other arguments that C<new_action> was supplied.
 
 C<MONIKER> is a unique designator of an action on a page.  The moniker
 is content-free and non-fattening, and may be auto-generated.  It is

Modified: jifty/branches/jifty-jsan/lib/Jifty/Web/Form/Field.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Web/Form/Field.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Web/Form/Field.pm	Thu May 11 22:41:39 2006
@@ -281,7 +281,8 @@
 Outputs this form element in a span with class C<form_field>.  This
 outputs the label, the widget itself, any hints, any errors, and any
 warnings using L</render_label>, L</render_widget>, L</render_hints>,
-L</render_errors> respectively.  Returns an empty string.
+L</render_errors>, and L</render_warnings>, respectively.  Returns an
+empty string.
 
 This is also what C<Jifty::Web::Form::Field>s do when stringified.
 

Modified: jifty/branches/jifty-jsan/lib/Jifty/Web/PageRegion.pm
==============================================================================
--- jifty/branches/jifty-jsan/lib/Jifty/Web/PageRegion.pm	(original)
+++ jifty/branches/jifty-jsan/lib/Jifty/Web/PageRegion.pm	Thu May 11 22:41:39 2006
@@ -294,7 +294,7 @@
     Jifty->handler->mason->interp->out_method( \$region_content );
 
     # Call into the dispatcher
-    Jifty->dispatcher->handle_request;
+    Jifty->handler->dispatcher->handle_request;
     $result .= $region_content;
     $result .= qq|</div>| if ( $self->region_wrapper );
 

Added: jifty/branches/jifty-jsan/plugins/EditInPlace/META.yml
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/plugins/EditInPlace/META.yml	Thu May 11 22:41:39 2006
@@ -0,0 +1,10 @@
+distribution_type: module
+generated_by: Module::Install version 0.610
+license: unknown
+name: Jifty-Plugin-EditInPlace
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Jifty: 0.60510

Added: jifty/branches/jifty-jsan/plugins/EditInPlace/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/plugins/EditInPlace/Makefile.PL	Thu May 11 22:41:39 2006
@@ -0,0 +1,9 @@
+use inc::Module::Install 0.46;
+name('Jifty-Plugin-EditInPlace');
+requires('Jifty' => '0.60510' );
+&auto_install();
+
+install_share;
+
+WriteAll;
+

Added: jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace.pm
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace.pm	Thu May 11 22:41:39 2006
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::EditInPlace;
+use base qw/Jifty::Plugin/;
+
+
+
+1;

Added: jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Action/FileEditor.pm
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Action/FileEditor.pm	Thu May 11 22:41:39 2006
@@ -0,0 +1,179 @@
+package Jifty::Plugin::EditInPlace::Action::FileEditor;
+
+use base qw/Jifty::Action/;
+use File::Spec;
+use File::Basename ();
+
+
+=head1 NAME
+
+Jifty::Plugin::EditInPlace::Action::FileEditor
+
+=head1 DESCRIPTION
+
+This action allows you to edit mason components (and eventually libraries)
+using Jifty's I<Action> system.  It should only be enabled when you're
+running Jifty in C<DevelMode>. 
+
+=head1 WARNING
+
+B<THIS ACTION LETS YOU REMOTELY EDIT EXECUTABLE CODE>.
+
+B<THIS IS DANGEROUS>
+
+=cut
+
+=head2 new
+
+Create a new C<FileEditor> action.
+
+=cut
+
+sub new {
+    my $class = shift; 
+    my $self = $class->SUPER::new(@_);
+    $self->sticky_on_success(1);
+    $self->get_default_content; 
+    return($self);
+
+}
+
+=head2 arguments
+
+Sets up this action's arguments.
+
+=over
+
+=item path
+
+Where to save the file
+
+=item file_type
+
+(One of mason_component or library)
+
+=item source_path
+
+Where to read the file from.
+
+=item destination_path
+
+Where to write the file to. If the current user can't write to 
+the source_path, defaults to something inside the app's directory.
+
+=item content
+
+The actual content of the file we're editing.
+
+=back
+
+=cut
+
+
+sub arguments {
+    my $self = shift;
+
+    {   file_type => {
+            default      => 'mason_component',
+            render_as    => 'Select',
+            valid_values => [qw/mason_component library/],
+            constructor  => 1
+        },
+        source_path      => { type => 'text', constructor => 1 },
+        destination_path => { type => 'text', ajax_validates=> 1, label => 'Save as' },
+        content => { render_as => 'Textarea', cols => 80, rows => 25 },
+
+    }
+
+}
+
+
+=head2 get_default_content
+
+Finds the version of the C<source_path> (of type C<file_type>) and
+loads it into C<content>.
+
+=cut
+
+sub get_default_content {
+    my $self = shift;
+
+    # Don't override content we already have
+    return if ($self->argument_value('content'));
+    my $path = $self->argument_value('source_path');
+    my $type = $self->argument_value('file_type');
+    my $out = '';
+    my %cfg = Jifty->handler->mason_config;
+    
+    my($local_template_base, $qualified_path);
+    if ($type eq "mason_component") {
+        foreach my $item (@{$cfg{comp_root}}) {
+            $local_template_base = $item->[1] if $item->[0] eq 'application';
+            $qualified_path = File::Spec->catfile($item->[1],$path);
+            # We want the first match
+            last if -f $qualified_path and -r $qualified_path;
+            undef $qualified_path;
+        }
+        $self->argument_value(destination_path => File::Spec->catfile($local_template_base, $path));
+    } else {
+        $qualified_path = "/$path" if -f "/$path" and -r "/$path";
+        $self->argument_value(destination_path => $qualified_path);
+    }
+
+    if ($qualified_path) {
+        local $/;
+        my $filehandle;
+        open ($filehandle, "<$qualified_path")||die "Couldn't read $qualified_path: $!";
+        $self->argument_value(content => <$filehandle>);
+        close($filehandle);
+    }
+}
+
+=head2 validate_destination_path PATH
+
+Returns true if the user can write to the directory C<PATH>. False
+otherwise. Should be refactored to a C<path_writable> routine and a
+trivial validator.
+
+=cut
+
+sub validate_destination_path {
+    my $self = shift;
+    my $value = shift;
+    $self->{'write_to'} = $value;
+    unless ($self->{'write_to'}) {
+        return  $self->validation_error( destination_path => "No destination path set. Where should I write this file?");
+    }
+    if (-f $self->{'write_to'} and not -w $self->{'write_to'}) {
+        return  $self->validation_error( destination_path => "Can't save the file to ".$self->{'write_to'});
+    }
+    return $self->validation_ok( 'write_to' );
+}
+
+
+=head2 take_action
+
+Writes the C<content> out to the C<destination_path>.
+
+=cut
+
+sub take_action {
+    my $self = shift;
+    my $dest  = $self->{'write_to'};
+
+    # discard filename. we only want to make the directory ;)
+    Jifty::Util->make_path( File::Basename::dirname( $dest ) );
+    my $writehandle = IO::File->new();
+
+    my $content = $self->argument_value('content');
+    $content =~ s/\r\n/\n/g;
+
+    $writehandle->open(">$dest") || die "Couldn't open $dest for writing: ".$!;
+    $writehandle->print( $content ) || die " Couldn't write to $dest: ".$!;
+    $writehandle->close() || die "Couldn't close filehandle $dest ".$!;
+    $self->result->message("Updated $dest");
+}
+
+
+
+1;

Added: jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/plugins/EditInPlace/lib/Jifty/Plugin/EditInPlace/Dispatcher.pm	Thu May 11 22:41:39 2006
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::EditInPlace::Dispatcher;
+use Jifty::Dispatcher -base;
+
+on qr'^/__jifty/edit/(.*?)/(.*)$', run {
+    my $editor = Jifty->web->new_action(
+        class     => 'Jifty::Plugin::EditInPlace::Action::FileEditor',
+        moniker   => 'editpage',
+        arguments => {
+            source_path => $2,
+            file_type   => $1,
+        }
+    );
+
+    set editor => $editor;
+    show '/__jifty/edit_file';
+};
+
+
+1;

Added: jifty/branches/jifty-jsan/plugins/EditInPlace/share/web/templates/__jifty/edit_file
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/plugins/EditInPlace/share/web/templates/__jifty/edit_file	Thu May 11 22:41:39 2006
@@ -0,0 +1,14 @@
+<%args>
+$path=> undef
+$editor => undef
+</%args>
+<%init>
+my $title = _("Editing file %1",$editor->argument_value('source_path'));
+</%init>
+<&|/_elements/wrapper, title => $title &>
+<% Jifty->web->form->start %>
+<%$editor->form_field('destination_path')%>
+<%$editor->form_field('content')%>
+<% Jifty->web->return(label => 'Save and return', submit => $editor )%>
+<% Jifty->web->form->end %>
+</&>

Modified: jifty/branches/jifty-jsan/share/web/static/js/jifty.js
==============================================================================
--- jifty/branches/jifty-jsan/share/web/static/js/jifty.js	(original)
+++ jifty/branches/jifty-jsan/share/web/static/js/jifty.js	Thu May 11 22:41:39 2006
@@ -105,22 +105,34 @@
                     function (request) {
                         var response  = request.responseXML.documentElement;
                         for (var action = response.firstChild; action != null; action = action.nextSibling) {
-                            if ((action.nodeName != 'action') || (action.getAttribute("id") != id))
-                                continue;
-                            for (var field = action.firstChild; field != null; field = field.nextSibling) {
-                                // Possibilities for field.nodeName: it could be #text (whitespace),
-                                // or 'blank' (the field was blank, don't mess with the error div), or 'ok'
-                                // (clear the error and warning div!) or 'error' (fill in the error div, clear 
-				// the warning div!) or 'warning' (fill in the warning div and clear the error div!)
-                                if (field.nodeName == 'error' || field.nodeName == 'warning') {
-                                    var err_div = document.getElementById(field.getAttribute("id"));
-                                    if (err_div != null) {
-                                        err_div.innerHTML = field.firstChild.data;
+                            if ((action.nodeName == 'validationaction') && (action.getAttribute("id") == id)) {
+                                for (var field = action.firstChild; field != null; field = field.nextSibling) {
+                                    // Possibilities for field.nodeName: it could be #text (whitespace),
+                                    // or 'blank' (the field was blank, don't mess with the error div), or 'ok'
+                                    // (clear the error and warning div!) or 'error' (fill in the error div, clear 
+                                    // the warning div!) or 'warning' (fill in the warning div and clear the error div!)
+                                    if (field.nodeName == 'error' || field.nodeName == 'warning') {
+                                        var err_div = document.getElementById(field.getAttribute("id"));
+                                        if (err_div != null) {
+                                            err_div.innerHTML = field.firstChild.data;
+                                        }
+                                    } else if (field.nodeName == 'ok') {
+                                        var err_div = document.getElementById(field.getAttribute("id"));
+                                        if (err_div != null) {
+                                            err_div.innerHTML = '';
+                                        }
                                     }
-                                } else if (field.nodeName == 'ok') {
-                                    var err_div = document.getElementById(field.getAttribute("id"));
-                                    if (err_div != null) {
-                                        err_div.innerHTML = '';
+                                }
+                            } else if ((action.nodeName == 'canonicalizeaction') && (action.getAttribute("id") == id)) {
+                                for (var field = action.firstChild; field != null; field = field.nextSibling) {
+                                    // Possibilities for field.nodeName: it could be 'ignored', 'blank' or 'update'
+                                    if (field.nodeName == 'update') {
+                                        var field_name = field.getAttribute("name");
+                                        for (var form_number in document.forms) {
+                                            if (document.forms[form_number].elements[field_name] == null)
+                                                continue;
+                                            document.forms[form_number].elements[field_name].value = field.firstChild.data;
+                                        }
                                     }
                                 }
                             }

Added: jifty/branches/jifty-jsan/share/web/templates/__jifty/error/autohandler
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/share/web/templates/__jifty/error/autohandler	Thu May 11 22:41:39 2006
@@ -0,0 +1,4 @@
+<%flags>
+inherit => undef
+</%flags>
+% $m->call_next
\ No newline at end of file

Modified: jifty/branches/jifty-jsan/share/web/templates/__jifty/validator.xml
==============================================================================
--- jifty/branches/jifty-jsan/share/web/templates/__jifty/validator.xml	(original)
+++ jifty/branches/jifty-jsan/share/web/templates/__jifty/validator.xml	Thu May 11 22:41:39 2006
@@ -8,7 +8,7 @@
 $writer->startTag("validation");
 for my $ra ( Jifty->web->request->actions ) {
     my $action = Jifty->web->new_action_from_request($ra);
-    $writer->startTag( "action", id => $action->register_name );
+    $writer->startTag( "validationaction", id => $action->register_name );
     for my $arg ( $action->argument_names ) {
         if ( not $action->arguments->{$arg}->{ajax_validates} ) {
             $writer->emptyTag( "ignored", id => $action->error_div_id($arg) );
@@ -37,6 +37,22 @@
         }
     }
     $writer->endTag();
+    $writer->startTag( "canonicalizeaction", id => $action->register_name );
+    for my $arg ( $action->argument_names ) {
+        if ( not $action->arguments->{$arg}->{ajax_canonicalizes} ) {
+            $writer->emptyTag( "ignored", name => $action->form_field_name($arg) );
+        } elsif ( not defined $action->argument_value($arg)
+            or length $action->argument_value($arg) == 0 ) {
+            $writer->emptyTag( "blank", name => $action->form_field_name($arg) );
+        } else {
+            $writer->dataElement(
+                "update",
+                $action->argument_value($arg),
+                name => $action->form_field_name($arg)
+            );
+        }
+    }
+    $writer->endTag();
 }
 $writer->endTag();
 $m->out($output);

Modified: jifty/branches/jifty-jsan/t/07-limit-actions.t
==============================================================================
--- jifty/branches/jifty-jsan/t/07-limit-actions.t	(original)
+++ jifty/branches/jifty-jsan/t/07-limit-actions.t	Thu May 11 22:41:39 2006
@@ -10,14 +10,12 @@
 
 =cut
 
-use Jifty::Test tests => 22;
+use Jifty::Test tests => 21;
 
 use_ok('Jifty::API');
 
 my $api = Jifty::API->new();
 
-is(Jifty->config->framework("ActionBasePath"), "JiftyApp::Action", "Action base path is as expected");
-
 ok($api->is_allowed("Jifty::Action::Autocomplete"), "Some Jifty actions are allowed");
 ok(!$api->is_allowed("Jifty::Action::Record::Update"), "Most are not");
 ok($api->is_allowed("Foo"), "Unqualified tasks default to positive limit");

Modified: jifty/branches/jifty-jsan/t/TestApp/lib/TestApp/Action/DoSomething.pm
==============================================================================
--- jifty/branches/jifty-jsan/t/TestApp/lib/TestApp/Action/DoSomething.pm	(original)
+++ jifty/branches/jifty-jsan/t/TestApp/lib/TestApp/Action/DoSomething.pm	Thu May 11 22:41:39 2006
@@ -2,6 +2,35 @@
 
 use base qw/Jifty::Action/;
 
+sub arguments {
+    return({
+        email => {
+            label => 'Email',
+            ajax_canonicalizes => 1,
+            ajax_validates => 1,
+        }
+    });
+}
+
+sub canonicalize_email {
+    my $self = shift;
+    my $address = shift;
+    
+    return lc($address);
+}
+
+sub validate_email {
+    my $self = shift;
+    my $address = shift;
+
+    if($address =~ /bad\@email\.com/) {
+        return $self->validation_error('email', "Bad looking email");
+    } elsif ($address =~ /warn\@email\.com/) {
+        return $self->validation_warning('email', "Warning for email");
+    }
+    return $self->validation_ok('email');
+}
+
 sub take_action {
     my $self = shift;
 

Added: jifty/branches/jifty-jsan/t/TestApp/t/06-validation.t
==============================================================================
--- (empty file)
+++ jifty/branches/jifty-jsan/t/TestApp/t/06-validation.t	Thu May 11 22:41:39 2006
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+
+BEGIN {chdir "t/TestApp"}
+use lib '../../lib';
+use Jifty::Test tests => 13;
+use Jifty::Test::WWW::Mechanize;
+
+my $server  = Jifty::Test->make_server;
+
+isa_ok($server, 'Jifty::Server');
+
+my $URL     = $server->started_ok;
+my $mech    = Jifty::Test::WWW::Mechanize->new();
+
+$mech->get_ok("$URL/__jifty/validator.xml?J:A-dosomething=TestApp::Action::DoSomething&J:A:F-email-dosomething=good\@address.com&J:VALIDATE=1&_=",
+    "Getting validator.xml output for valid form entry");
+$mech->content_contains('<ok id="errors-J:A:F-email-dosomething" />',
+    " ... validator returned ok for errors");
+$mech->content_contains('<ok id="warnings-J:A:F-email-dosomething" />',
+    " ... validator returned ok for warnings");
+
+$mech->get_ok("$URL/__jifty/validator.xml?J:A-dosomething=TestApp::Action::DoSomething&J:A:F-email-dosomething=warn\@email.com&J:VALIDATE=1&_=",
+    "Getting validator.xml output for a warning form entry");
+$mech->content_contains('<ok id="errors-J:A:F-email-dosomething" />',
+    " ... validator returned ok for errors");
+$mech->content_contains('<warning id="warnings-J:A:F-email-dosomething">Warning for email</warning>',
+    " ... validator returned warning");
+
+$mech->get_ok("$URL/__jifty/validator.xml?J:A-dosomething=TestApp::Action::DoSomething&J:A:F-email-dosomething=bad\@email.com&J:VALIDATE=1&_=",
+    "Getting validator.xml output for a warning form entry");
+$mech->content_contains('<error id="errors-J:A:F-email-dosomething">Bad looking email</error>',
+    " ... validator returned error");
+$mech->content_contains('<ok id="warnings-J:A:F-email-dosomething" />',
+    " ... validator returned ok for warnings");
+
+$mech->get_ok("$URL/__jifty/validator.xml?J:A-dosomething=TestApp::Action::DoSomething&J:A:F-email-dosomething=UPPER\@EMAIL.com&J:VALIDATE=1&_=",
+    "Getting validator.xml output for a canonicalization");
+$mech->content_contains('<update name="J:A:F-email-dosomething">upper at email.com</update>',
+    " ... canonicalizer returned all lower case (good)");
+


More information about the Jifty-commit mailing list