[Jifty-commit] r3459 - in jifty/branches/virtual-models: . bin debian examples/Example-Todo examples/Yada examples/Yada/etc examples/Yada/inc/Module examples/Yada/inc/Module/Install examples/Yada/lib/Yada examples/Yada/lib/Yada/Action examples/Yada/lib/Yada/Model examples/Yada/lib/Yada/View examples/Yada/t inc/Module inc/Module/Install lib lib/Jifty lib/Jifty/Action lib/Jifty/Manual lib/Jifty/Module lib/Jifty/Plugin lib/Jifty/Plugin/Authentication/Password lib/Jifty/Plugin/Authentication/Password/Action lib/Jifty/Plugin/Authentication/Password/Mixin/Model lib/Jifty/Plugin/CompressedCSSandJS lib/Jifty/Plugin/Feedback lib/Jifty/Plugin/Feedback/Action lib/Jifty/Plugin/OpenID lib/Jifty/Plugin/OpenID/Action lib/Jifty/Plugin/OpenID/Mixin lib/Jifty/Plugin/OpenID/Mixin/Model lib/Jifty/Plugin/OpenID/Model lib/Jifty/Plugin/SinglePage lib/Jifty/Plugin/SiteNews/Mixin/Model lib/Jifty/Plugin/SiteNews/View lib/Jifty/Plugin/SkeletonApp lib/Jifty/Plugin/TabView lib/Jifty/Script lib/Jifty/View/Declare lib/Jifty/View/Mason lib/Jifty/Web lib/Jifty/Web/Form plugins/CodePress plugins/CodePress/doc plugins/CodePress/lib plugins/CodePress/lib/Jifty plugins/CodePress/lib/Jifty/Plugin plugins/CodePress/lib/Jifty/Plugin/CodePress plugins/CodePress/lib/Jifty/Plugin/CodePress/Model plugins/CodePress/share plugins/CodePress/share/web plugins/CodePress/share/web/static plugins/CodePress/share/web/static/codepress plugins/CodePress/share/web/static/codepress/engines plugins/CodePress/share/web/static/codepress/images plugins/CodePress/share/web/static/codepress/languages plugins/CodePress/share/web/static/js plugins/CodePress/t share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin share/po share/web/static/js share/web/templates/__jifty share/web/templates/__jifty/webservices share/web/templates/_elements t t/TestApp-Plugin-News t/TestApp-Plugin-News/bin t/TestApp-Plugin-News/doc t/TestApp-Plugin-News/etc t/TestApp-Plugin-News/lib t/TestApp-Plugin-News/lib/TestApp t/TestApp-Plugin-News/lib/TestApp/Plugin t/TestApp-Plugin-News/lib/TestApp/Plugin/News t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Action t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Model t/TestApp-Plugin-News/log t/TestApp-Plugin-News/share t/TestApp-Plugin-News/share/po t/TestApp-Plugin-News/share/web t/TestApp-Plugin-News/share/web/static t/TestApp-Plugin-News/share/web/templates t/TestApp-Plugin-News/t t/TestApp-Plugin-News/var t/TestApp-Plugin-News/var/mason t/TestApp/lib/TestApp t/TestApp/lib/TestApp/Model t/TestApp/lib/TestApp/View t/TestApp/t t/lib/Jifty

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Tue Jun 12 15:48:17 EDT 2007


Author: sterling
Date: Tue Jun 12 15:48:07 2007
New Revision: 3459

Added:
   jifty/branches/virtual-models/examples/Yada/
      - copied from r3457, /jifty/branches/virtual-models/examples/Example-Todo/
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Can.pm
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Fetch.pm
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Makefile.pm
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Win32.pm
   jifty/branches/virtual-models/examples/Yada/lib/Yada/
   jifty/branches/virtual-models/examples/Yada/lib/Yada/Action/
   jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/
   jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/Todo.pm
   jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/User.pm
   jifty/branches/virtual-models/examples/Yada/lib/Yada/View/
   jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm
   jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/
   jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/
   jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm   (contents, props changed)
   jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/View.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/AuthenticateOpenID.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/VerifyOpenID.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Mixin/
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Mixin/Model/
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Model/
   jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/View.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage/
   jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/TabView/
   jifty/branches/virtual-models/lib/Jifty/Plugin/TabView/View.pm
   jifty/branches/virtual-models/lib/Jifty/Schema.pm
   jifty/branches/virtual-models/lib/Jifty/Script/Console.pm
   jifty/branches/virtual-models/lib/Jifty/View.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm
   jifty/branches/virtual-models/plugins/CodePress/
   jifty/branches/virtual-models/plugins/CodePress/Makefile.PL
   jifty/branches/virtual-models/plugins/CodePress/doc/
   jifty/branches/virtual-models/plugins/CodePress/doc/index.html
   jifty/branches/virtual-models/plugins/CodePress/lib/
   jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/
   jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/
   jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress/
   jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm
   jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress/Model/
   jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress/Textarea.pm
   jifty/branches/virtual-models/plugins/CodePress/share/
   jifty/branches/virtual-models/plugins/CodePress/share/po/
   jifty/branches/virtual-models/plugins/CodePress/share/web/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/codepress.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/codepress.html
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/gecko.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/khtml.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/msie.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/older.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/opera.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/images/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/images/line-numbers.png   (contents, props changed)
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/asp.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/asp.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/csharp.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/csharp.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/css.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/css.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/generic.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/generic.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/html.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/html.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/java.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/java.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/javascript.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/javascript.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/perl.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/perl.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/php.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/php.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/ruby.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/ruby.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/sql.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/sql.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/text.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/text.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/vbscript.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/vbscript.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/xsl.css
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/xsl.js
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/js/
   jifty/branches/virtual-models/plugins/CodePress/share/web/static/js/codepress.js
   jifty/branches/virtual-models/plugins/CodePress/t/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/Makefile.PL
   jifty/branches/virtual-models/t/TestApp-Plugin-News/bin/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/bin/jifty   (contents, props changed)
   jifty/branches/virtual-models/t/TestApp-Plugin-News/doc/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/etc/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/etc/config.yml
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Action/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Model/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Model/News.pm
   jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/View.pm
   jifty/branches/virtual-models/t/TestApp-Plugin-News/log/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/share/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/share/po/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/share/web/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/share/web/static/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/share/web/templates/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/t/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/var/
   jifty/branches/virtual-models/t/TestApp-Plugin-News/var/mason/
   jifty/branches/virtual-models/t/TestApp/t/16-template-region.t
   jifty/branches/virtual-models/t/TestApp/t/17-template-region-internal-redirect.t
   jifty/branches/virtual-models/t/TestApp/t/before_access.t
Removed:
   jifty/branches/virtual-models/examples/Example-Todo/
Modified:
   jifty/branches/virtual-models/   (props changed)
   jifty/branches/virtual-models/AUTHORS
   jifty/branches/virtual-models/META.yml
   jifty/branches/virtual-models/Makefile.PL
   jifty/branches/virtual-models/SIGNATURE
   jifty/branches/virtual-models/bin/jifty
   jifty/branches/virtual-models/debian/control
   jifty/branches/virtual-models/examples/Yada/META.yml
   jifty/branches/virtual-models/examples/Yada/Makefile.PL
   jifty/branches/virtual-models/examples/Yada/etc/config.yml
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install.pm
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Base.pm
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Metadata.pm
   jifty/branches/virtual-models/examples/Yada/inc/Module/Install/WriteAll.pm
   jifty/branches/virtual-models/examples/Yada/t/00-model-Todo.t
   jifty/branches/virtual-models/inc/Module/Install.pm
   jifty/branches/virtual-models/inc/Module/Install/AutoInstall.pm
   jifty/branches/virtual-models/inc/Module/Install/Base.pm
   jifty/branches/virtual-models/inc/Module/Install/Can.pm
   jifty/branches/virtual-models/inc/Module/Install/Fetch.pm
   jifty/branches/virtual-models/inc/Module/Install/Include.pm
   jifty/branches/virtual-models/inc/Module/Install/Makefile.pm
   jifty/branches/virtual-models/inc/Module/Install/Metadata.pm
   jifty/branches/virtual-models/inc/Module/Install/Scripts.pm
   jifty/branches/virtual-models/inc/Module/Install/Share.pm
   jifty/branches/virtual-models/inc/Module/Install/Win32.pm
   jifty/branches/virtual-models/inc/Module/Install/WriteAll.pm
   jifty/branches/virtual-models/lib/Jifty.pm
   jifty/branches/virtual-models/lib/Jifty/API.pm
   jifty/branches/virtual-models/lib/Jifty/Action.pm
   jifty/branches/virtual-models/lib/Jifty/Action/Record.pm
   jifty/branches/virtual-models/lib/Jifty/ClassLoader.pm
   jifty/branches/virtual-models/lib/Jifty/Config.pm
   jifty/branches/virtual-models/lib/Jifty/Continuation.pm
   jifty/branches/virtual-models/lib/Jifty/CurrentUser.pm
   jifty/branches/virtual-models/lib/Jifty/DateTime.pm
   jifty/branches/virtual-models/lib/Jifty/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/I18N.pm
   jifty/branches/virtual-models/lib/Jifty/LetMe.pm
   jifty/branches/virtual-models/lib/Jifty/Manual/Continuations.pod
   jifty/branches/virtual-models/lib/Jifty/Manual/Cookbook.pod
   jifty/branches/virtual-models/lib/Jifty/Manual/Models.pod
   jifty/branches/virtual-models/lib/Jifty/Module/Pluggable.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/View.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/View/News.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm
   jifty/branches/virtual-models/lib/Jifty/Record.pm
   jifty/branches/virtual-models/lib/Jifty/Request.pm
   jifty/branches/virtual-models/lib/Jifty/RightsFrom.pm
   jifty/branches/virtual-models/lib/Jifty/Script/App.pm
   jifty/branches/virtual-models/lib/Jifty/Script/Plugin.pm
   jifty/branches/virtual-models/lib/Jifty/Script/Schema.pm
   jifty/branches/virtual-models/lib/Jifty/Script/Server.pm
   jifty/branches/virtual-models/lib/Jifty/Server.pm
   jifty/branches/virtual-models/lib/Jifty/Util.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Handler.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm
   jifty/branches/virtual-models/lib/Jifty/View/Mason/Handler.pm
   jifty/branches/virtual-models/lib/Jifty/Web.pm
   jifty/branches/virtual-models/lib/Jifty/Web/Form.pm
   jifty/branches/virtual-models/lib/Jifty/Web/Form/Clickable.pm
   jifty/branches/virtual-models/lib/Jifty/Web/Form/Element.pm
   jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm
   jifty/branches/virtual-models/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html
   jifty/branches/virtual-models/share/po/en.po
   jifty/branches/virtual-models/share/po/zh_cn.po
   jifty/branches/virtual-models/share/web/static/js/calendar.js
   jifty/branches/virtual-models/share/web/static/js/jifty.js
   jifty/branches/virtual-models/share/web/static/js/key_bindings.js
   jifty/branches/virtual-models/share/web/templates/__jifty/autocomplete.xml
   jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml
   jifty/branches/virtual-models/share/web/templates/_elements/sidebar
   jifty/branches/virtual-models/t/01-dependencies.t
   jifty/branches/virtual-models/t/TestApp/lib/TestApp/Dispatcher.pm
   jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm
   jifty/branches/virtual-models/t/TestApp/lib/TestApp/View.pm
   jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/base.pm
   jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/instance.pm
   jifty/branches/virtual-models/t/TestApp/t/09-redirect.t
   jifty/branches/virtual-models/t/TestApp/t/11-current_user.t
   jifty/branches/virtual-models/t/TestApp/t/15-template-subclass.t
   jifty/branches/virtual-models/t/lib/Jifty/SubTest.pm

Log:
 r7553 at riddle:  andrew | 2007-06-12 14:28:06 -0500
 Merging latest trunk into virtual-models branch. There are no new errors in the tests despite the size of this update.


Modified: jifty/branches/virtual-models/AUTHORS
==============================================================================
--- jifty/branches/virtual-models/AUTHORS	(original)
+++ jifty/branches/virtual-models/AUTHORS	Tue Jun 12 15:48:07 2007
@@ -28,3 +28,5 @@
 David Brunton <dbrunton at yahoo.com>
 Marc Mims <marc at questright.com>
 Alexander Klink <alech at cpan.org>
+Andreas Koenig <andreas.koenig.7os6VVqR at franz.ak.mind.de>
+sunnavy <sunnavy at gmail.com>

Modified: jifty/branches/virtual-models/META.yml
==============================================================================
--- jifty/branches/virtual-models/META.yml	(original)
+++ jifty/branches/virtual-models/META.yml	Tue Jun 12 15:48:07 2007
@@ -2,7 +2,7 @@
 build_requires: 
   ExtUtils::MakeMaker: 6.11
 distribution_type: module
-generated_by: Module::Install version 0.650
+generated_by: Module::Install version 0.670
 license: Perl
 meta-spec: 
   url: http://module-build.sourceforge.net/META-spec-v1.3.html
@@ -13,18 +13,24 @@
     - debian
     - doc
     - examples
+    - share
+    - plugins/*/t
     - inc
     - t
   package: 
     - DB
 recommends: 
   Apache2::Const: 0
+  Cache::FileCache: 0
   Class::Accessor::Named: 0
   DBD::SQLite: 0
   Devel::Cover: 0
+  Devel::EvalContext: 0
+  LWPx::ParanoidAgent: 0
   Module::CoreList: 0
   Module::Install::Admin: 0.50
   Module::Refresh: 0.09
+  Net::OpenID::Consumer: 0
   Net::Server::Fork: 0
   Net::Server::PreFork: 0
   PAR::Dist::FromCPAN: 0
@@ -43,6 +49,7 @@
   Class::Accessor: 0
   Class::Container: 0
   Class::Data::Inheritable: 0
+  Class::Trigger: 0
   Compress::Zlib: 0
   Crypt::CBC: 0
   Crypt::Rijndael: 0
@@ -82,7 +89,7 @@
   Log::Log4perl: 1.04
   MIME::Types: 0
   Module::CoreList: 0
-  Module::Pluggable: 3.1
+  Module::Pluggable: 3.5
   Module::Pluggable::Object: 0
   Module::Refresh: 0
   Module::ScanDeps: 0
@@ -95,6 +102,7 @@
   String::Koremutake: 0
   Template::Declare: 0.07
   Test::Base: 0
+  Test::LongString: 0
   Test::More: 0.62
   Test::Pod::Coverage: 0
   Test::WWW::Mechanize: 1.04
@@ -109,4 +117,4 @@
   perl: 5.8.3
   version: 0
 tests: t/*.t t/*/t/*.t
-version: 0.70415
+version: 0.70422

Modified: jifty/branches/virtual-models/Makefile.PL
==============================================================================
--- jifty/branches/virtual-models/Makefile.PL	(original)
+++ jifty/branches/virtual-models/Makefile.PL	Tue Jun 12 15:48:07 2007
@@ -8,6 +8,7 @@
 requires('Class::Accessor'); # Class::Accessor::Fast
 requires('Class::Container');
 requires('Class::Data::Inheritable');
+requires('Class::Trigger');
 requires('CGI' => '3.19');
 requires('CGI::Cookie::Splitter');
 requires('Crypt::CBC');
@@ -42,13 +43,14 @@
 requires('Hash::Merge');
 requires('Hook::LexWrap');
 requires('IPC::PubSub' => '0.23' );
+requires('IPC::Run3');
 requires('Jifty::DBI' => '0.40' );            # Jifty::DBI::Collection Jifty::DBI::Handle Jifty::DBI::Record::Cachable Jifty::DBI::SchemaGenerator
 requires('Locale::Maketext::Extract' => '0.20');
 requires('Locale::Maketext::Lexicon' => '0.60');
 requires('Log::Log4perl' => '1.04');
 requires('LWP::UserAgent'); # Net::HTTP
 requires('MIME::Types');
-requires('Module::Pluggable' => '3.1'); # Module::Pluggable::Object
+requires('Module::Pluggable' => '3.5'); # Module::Pluggable::Object
 requires('Module::Pluggable::Object');
 requires('Module::CoreList');
 requires('Module::Refresh');
@@ -64,6 +66,7 @@
 requires('SVN::Simple::Edit');
 requires('Template::Declare' => '0.07');                # Template::Declare::Tags
 requires('Test::Base');
+requires('Test::LongString');
 requires('Test::More' => 0.62 ),
 requires('Test::Pod::Coverage'),
 requires('Test::WWW::Mechanize' => 1.04 ),
@@ -128,6 +131,16 @@
         -default => 0,
         recommends('PAR::Dist::FromCPAN'),
     ],
+    'OpenID Login Plugin' => [
+        -default => 0,
+        recommends('Net::OpenID::Consumer'),
+        recommends('Cache::FileCache'),
+        recommends('LWPx::ParanoidAgent'),
+    ],
+    'Jifty console' => [
+        -default => 1,
+        recommends('Devel::EvalContext')
+    ],
 );
 
 

Modified: jifty/branches/virtual-models/SIGNATURE
==============================================================================
--- jifty/branches/virtual-models/SIGNATURE	(original)
+++ jifty/branches/virtual-models/SIGNATURE	Tue Jun 12 15:48:07 2007
@@ -18,8 +18,8 @@
 SHA1 876166ca6f2cf7bfa6a6137070beb1b55b4bc4ce Changelog
 SHA1 ed26a91a1edcc0aed0509bc7384b2d24cfd13d89 MANIFEST
 SHA1 d4adbf5948041cd460da5cb7ad21394a790e2022 MANIFEST.SKIP
-SHA1 85f15b04e047acd8c86edfca3f7760fa4c001584 META.yml
-SHA1 c11c8ba944d2d9410d04593112e7ab951648d4fd Makefile.PL
+SHA1 f1583a94e31ca844b8d9e2fcd4a239570495c6d3 META.yml
+SHA1 371fc084afa58a74bed4dee9bd09d5b1c544139c Makefile.PL
 SHA1 e395a2eabaf8faf8266dedc664c1eb52c6c589cf README
 SHA1 aaf8f7a1025fc97077072672f325e2a5f3c03a41 bin/build_par
 SHA1 f7f44f9a7337def0c97f981073e3ed970851d9ae bin/jifty
@@ -125,18 +125,18 @@
 SHA1 53c8822ddf426e82fe239e4470574913411b1355 examples/Ping/t/00compile.t
 SHA1 6d6c378447f9d74d53c06a2a027736b48b5d0670 examples/Ping/t/01startup.t
 SHA1 603bb9de29fb8cba7f13409c546750972eff645d inc/Module/AutoInstall.pm
-SHA1 ceac99579ef3914de1936417282842c07d907a6a inc/Module/Install.pm
-SHA1 c289fbaf0a2bce39c4993dab38fc70cbb3789f57 inc/Module/Install/AutoInstall.pm
-SHA1 6c9471c92c28e247fb84f4ab7dc277f68b33ea95 inc/Module/Install/Base.pm
-SHA1 b2cb1026330e8c4f8533c79569498ab15f189d86 inc/Module/Install/Can.pm
-SHA1 78acbad45c4289c228ead2278fc213ddd66bcead inc/Module/Install/Fetch.pm
-SHA1 a4b4488af13b5d038bb6fbd3c4ec635d2c640567 inc/Module/Install/Include.pm
-SHA1 642e5b27f4cbbbe440e5dc05c639f5fb79875fe3 inc/Module/Install/Makefile.pm
-SHA1 80d024f3eccf1c825b02b5e59de236b0af7d4bfc inc/Module/Install/Metadata.pm
-SHA1 a748c6176a996e5f32c50f2c49670a8ce532fb71 inc/Module/Install/Scripts.pm
-SHA1 f763d4fe06ca0876fa047a719c2fcf110a92b1d0 inc/Module/Install/Share.pm
-SHA1 fe6780ea5cfd67c79140699fbd4f0fe262255d57 inc/Module/Install/Win32.pm
-SHA1 51d43bffcfd7ffdecb8c8e9f97f3896c31b2e1b2 inc/Module/Install/WriteAll.pm
+SHA1 9b2f9d83bcf77860f53a0c07c90a4a59ad9f5df1 inc/Module/Install.pm
+SHA1 ad955f51ad2c40d4ba35395c27f5ed899a80bf7a inc/Module/Install/AutoInstall.pm
+SHA1 abe32855d75ab13747cf65765af9947b7a8c3057 inc/Module/Install/Base.pm
+SHA1 95b81d1e91bd634467bf633571eff4420e9c04eb inc/Module/Install/Can.pm
+SHA1 1fe98c63cf9d7271c8cb4183ba230f152df69e26 inc/Module/Install/Fetch.pm
+SHA1 0606a8b02a420600bc3e2b65ab82f70266784926 inc/Module/Install/Include.pm
+SHA1 2249171a2b72cd73ff2c0a06597d29f86e5df456 inc/Module/Install/Makefile.pm
+SHA1 381bb98ea3877bba49ae85e7a7ea130645fd3dbf inc/Module/Install/Metadata.pm
+SHA1 b384bd42bc6263dc56d04a884ef4e5fe340049f8 inc/Module/Install/Scripts.pm
+SHA1 fcae3a3bda09e6ba955c8746c2bdf582a23b8d56 inc/Module/Install/Share.pm
+SHA1 0c2118868ef82ac517eb6d9c3bd93e6eb9bbf83e inc/Module/Install/Win32.pm
+SHA1 e827d6d43771032fa3df35c0ad5e5698d0e54cda inc/Module/Install/WriteAll.pm
 SHA1 c17e8f3cf8ebe1eb4929fd2bd2fd530a9de1abd0 lib/Email/Send/Jifty/Test.pm
 SHA1 0eb6271f949efa613d516c73cb672f7392c336b5 lib/Jifty.pm
 SHA1 337627c441c5639405a2d2cc751c63616d25c221 lib/Jifty/API.pm
@@ -161,7 +161,7 @@
 SHA1 fb9f33e2838fbff0cd5b5a784adee8b0fc347ebc lib/Jifty/Event/Model.pm
 SHA1 121cc604741f5a674cbbc2a55dfb6d4c8cf11bb8 lib/Jifty/Everything.pm
 SHA1 818bd0aa6afeb39bf96e0068fe3222c74133b4d8 lib/Jifty/Filter/DateTime.pm
-SHA1 45fa3790eb4737497dd728510cc92aa76017e350 lib/Jifty/Handle.pm
+SHA1 53c0eb3380e3f1611866694a6e1294f3537f0d76 lib/Jifty/Handle.pm
 SHA1 947e5aee7b981e19042f3343050a129600b88772 lib/Jifty/Handler.pm
 SHA1 c413c80506fe0a1da3154739249a9351982b1db2 lib/Jifty/I18N.pm
 SHA1 fe370e2c51ca0f20cb0bce133b8c9067c02a524a lib/Jifty/JSON.pm
@@ -258,7 +258,7 @@
 SHA1 e15bde68ba67b141ae1565be066f1e1f171f63ec lib/Jifty/Test.pm
 SHA1 0d6d53b5084bb89baea771d3e9d602a195255e37 lib/Jifty/Test/WWW/Mechanize.pm
 SHA1 47c5840fafd56473a0e1a9228f169d3813317c13 lib/Jifty/TestServer.pm
-SHA1 4614193951790ed53fc8682135cdb1b40d3fa975 lib/Jifty/Upgrade.pm
+SHA1 a0ad0b8d54a5ed0edbbac99f6c06a9438a4046fa lib/Jifty/Upgrade.pm
 SHA1 f3957ac04ad92737d68b2a63f068679b4bacafed lib/Jifty/Upgrade/Internal.pm
 SHA1 d42ff4531e36438208cd8242d15f7d210928ff6b lib/Jifty/Util.pm
 SHA1 05af258e63bbb8ea166e845b8028546b858fcecb lib/Jifty/View/Declare.pm
@@ -1053,7 +1053,7 @@
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.3 (Darwin)
 
-iD8DBQFGLAX/Ei9d9xCOQEYRAnSsAKC6QQRZGQFzdh1RYF6eRAfYxjg1KQCfU8ih
-Lh9Xz380qhLKc6AxFRUaZkM=
-=RTdf
+iD8DBQFGLAc7Ei9d9xCOQEYRAhCwAJ4lMvEguYMYUKn5vsKqG/5r5NzLeACfdTbM
+ErSkhd0sSOnf65Oqb8gHWzg=
+=cCbP
 -----END PGP SIGNATURE-----

Modified: jifty/branches/virtual-models/bin/jifty
==============================================================================
--- jifty/branches/virtual-models/bin/jifty	(original)
+++ jifty/branches/virtual-models/bin/jifty	Tue Jun 12 15:48:07 2007
@@ -4,12 +4,8 @@
 use File::Basename qw(dirname); 
 use UNIVERSAL::require;
 
-BEGIN {
-    Jifty::Util->require or die $UNIVERSAL::require::ERROR;
-    my $root = Jifty::Util->app_root;
-    unshift @INC, "$root/lib" if ($root);
-}
-
+use Jifty;
 use Jifty::Script;
+
 local $SIG{INT} = sub { warn "Stopped\n"; exit; };
 Jifty::Script->dispatch();

Modified: jifty/branches/virtual-models/debian/control
==============================================================================
--- jifty/branches/virtual-models/debian/control	(original)
+++ jifty/branches/virtual-models/debian/control	Tue Jun 12 15:48:07 2007
@@ -53,8 +53,10 @@
  perl-modules, libcgi-cookie-splitter-perl, libcgi-simple-perl,
  libcrypt-cbc-perl, libcrypt-rijndael-perl,
  libcompress-zlib-perl, libcss-squish-perl (>> 0.05), 
+ libclass-trigger-perl, libdevel-evalcontext-perl,
  libdbd-sqlite3-perl, libdata-page-perl, libossp-uuid-perl,
- libdatetime-perl, libdate-manip-perl, libemail-folder-perl,
+ libdatetime-perl, libdatetime-format-builder-perl, 
+ libdate-manip-perl, libemail-folder-perl,
  libemail-localdelivery-perl, libemail-mime-perl,
  libemail-mime-creator-perl, libemail-mime-contenttype-perl,
  libemail-send-perl (>> 1.99_01), libemail-simple-perl,
@@ -65,8 +67,8 @@
  libwww-perl, libhttp-server-simple-perl (>> 0.26), 
  libhttp-server-simple-recorder-perl, libhash-merge-perl, libhook-lexwrap-perl,
  libipc-pubsub-perl (>> 0.23), libjifty-dbi-perl (>> 0.40),
- liblocale-maketext-lexicon-perl, liblog-log4perl-perl,
- libmime-types-perl, libmodule-pluggable-perl (>> 3.1),
+ liblocale-maketext-lexicon-perl, liblocale-maketext-simple-perl, liblog-log4perl-perl,
+ libmime-types-perl, libmodule-pluggable-perl (>> 3.5),
  libmodule-corelist-perl, libmodule-refresh-perl,
  libmodule-scandeps-perl, libobject-declare-perl (>> 0.22),
  libparams-validate-perl, libscalar-defer-perl (>> 0.10),

Modified: jifty/branches/virtual-models/examples/Yada/META.yml
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/META.yml	(original)
+++ jifty/branches/virtual-models/examples/Yada/META.yml	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,15 @@
+--- 
+distribution_type: module
+generated_by: Module::Install version 0.67
+license: unknown
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Yada
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Jifty: 0.70117
+version: 0.01

Modified: jifty/branches/virtual-models/examples/Yada/Makefile.PL
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/Makefile.PL	(original)
+++ jifty/branches/virtual-models/examples/Yada/Makefile.PL	Tue Jun 12 15:48:07 2007
@@ -1,6 +1,6 @@
 use inc::Module::Install;
 
-name        'Example::Todo';
+name        'Yada';
 version     '0.01';
 requires    'Jifty' => '0.70117';
 

Modified: jifty/branches/virtual-models/examples/Yada/etc/config.yml
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/etc/config.yml	(original)
+++ jifty/branches/virtual-models/examples/Yada/etc/config.yml	Tue Jun 12 15:48:07 2007
@@ -1,19 +1,22 @@
 --- 
+application:
+  OpenIDSecret: sekrit13
 framework: 
   AdminMode: 1
-  ApplicationClass: Example::Todo
-  ApplicationName: Example-Todo
+  SkipAccessControl: 1
+  ApplicationClass: Yada
+  ApplicationName: Yada
   ApplicationUUID: 80DF397A-D999-11DB-B80A-9318EBB6763A
   Database: 
     CheckSchema: 1
-    Database: example_todo
+    Database: yada
     Driver: SQLite
     Host: localhost
     Password: ''
     RecordBaseClass: Jifty::DBI::Record::Cachable
     User: ''
     Version: 0.0.1
-  DevelMode: 1
+  DevelMode: 0
   L10N: 
     PoDir: share/po
   LogLevel: INFO
@@ -21,31 +24,21 @@
   MailerArgs: []
 
   Plugins: 
-    - 
-      SkeletonApp: {}
-
-    - 
-      REST: {}
-
-    - 
-      Halo: {}
-
-    - 
-      ErrorTemplates: {}
-
-    - 
-      OnlineDocs: {}
-
-    - 
-      CompressedCSSandJS: {}
-
-    - 
-      AdminUI: {}
+    - SkeletonApp: {}
+    - REST: {}
+    - ErrorTemplates: {}
+    - OnlineDocs: {}
+    - CompressedCSSandJS: {}
+    - AdminUI: {}
+    - LetMe: {}
+    - User: {}
+    - Authentication::Password: {}
+    - OpenID: {}
 
   PubSub: 
     Backend: Memcached
     Enable: ~
-  TemplateClass: Example::Todo::View
+  TemplateClass: Yada::View
   Web: 
     BaseURL: http://localhost
     DataDir: var/mason

Modified: jifty/branches/virtual-models/examples/Yada/inc/Module/Install.pm
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/inc/Module/Install.pm	(original)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install.pm	Tue Jun 12 15:48:07 2007
@@ -28,7 +28,7 @@
     # This is not enforced yet, but will be some time in the next few
     # releases once we can make sure it won't clash with custom
     # Module::Install extensions.
-    $VERSION = '0.64';
+    $VERSION = '0.67';
 }
 
 # Whether or not inc::Module::Install is actually loaded, the
@@ -86,7 +86,7 @@
             # delegate back to parent dirs
             goto &$code unless $cwd eq $pwd;
         }
-        $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+        $$sym =~ /([^:]+)$/ or Carp::confess "Cannot autoload $who - $sym";
         unshift @_, ($self, $1);
         goto &{$self->can('call')} unless uc($1) eq $1;
     };

Modified: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Base.pm
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/inc/Module/Install/Base.pm	(original)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Base.pm	Tue Jun 12 15:48:07 2007
@@ -1,7 +1,7 @@
 #line 1
 package Module::Install::Base;
 
-$VERSION = '0.64';
+$VERSION = '0.67';
 
 # Suspend handler for "redefined" warnings
 BEGIN {

Added: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Can.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Can.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,82 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Module::Install::Base;
+use Config ();
+### This adds a 5.005 Perl version dependency.
+### This is a bug and will be fixed.
+use File::Spec ();
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+	my ($self, $mod, $ver) = @_;
+	$mod =~ s{::|\\}{/}g;
+	$mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+	my $pkg = $mod;
+	$pkg =~ s{/}{::}g;
+	$pkg =~ s{\.pm$}{}i;
+
+	local $@;
+	eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# check if we can run some command
+sub can_run {
+	my ($self, $cmd) = @_;
+
+	my $_cmd = $cmd;
+	return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+	for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+		my $abs = File::Spec->catfile($dir, $_[1]);
+		return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+	}
+
+	return;
+}
+
+# can we locate a (the) C compiler
+sub can_cc {
+	my $self   = shift;
+	my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+	# $Config{cc} may contain args; try to find out the program part
+	while (@chunks) {
+		return $self->can_run("@chunks") || (pop(@chunks), next);
+	}
+
+	return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+	require ExtUtils::MM_Cygwin;
+	require ExtUtils::MM_Win32;
+	if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+		*ExtUtils::MM_Cygwin::maybe_command = sub {
+			my ($self, $file) = @_;
+			if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+				ExtUtils::MM_Win32->maybe_command($file);
+			} else {
+				ExtUtils::MM_Unix->maybe_command($file);
+			}
+		}
+	}
+}
+
+1;
+
+__END__
+
+#line 157

Added: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Fetch.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Fetch.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,93 @@
+#line 1
+package Module::Install::Fetch;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub get_file {
+    my ($self, %args) = @_;
+    my ($scheme, $host, $path, $file) = 
+        $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+
+    if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
+        $args{url} = $args{ftp_url}
+            or (warn("LWP support unavailable!\n"), return);
+        ($scheme, $host, $path, $file) = 
+            $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+    }
+
+    $|++;
+    print "Fetching '$file' from $host... ";
+
+    unless (eval { require Socket; Socket::inet_aton($host) }) {
+        warn "'$host' resolve failed!\n";
+        return;
+    }
+
+    return unless $scheme eq 'ftp' or $scheme eq 'http';
+
+    require Cwd;
+    my $dir = Cwd::getcwd();
+    chdir $args{local_dir} or return if exists $args{local_dir};
+
+    if (eval { require LWP::Simple; 1 }) {
+        LWP::Simple::mirror($args{url}, $file);
+    }
+    elsif (eval { require Net::FTP; 1 }) { eval {
+        # use Net::FTP to get past firewall
+        my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
+        $ftp->login("anonymous", 'anonymous at example.com');
+        $ftp->cwd($path);
+        $ftp->binary;
+        $ftp->get($file) or (warn("$!\n"), return);
+        $ftp->quit;
+    } }
+    elsif (my $ftp = $self->can_run('ftp')) { eval {
+        # no Net::FTP, fallback to ftp.exe
+        require FileHandle;
+        my $fh = FileHandle->new;
+
+        local $SIG{CHLD} = 'IGNORE';
+        unless ($fh->open("|$ftp -n")) {
+            warn "Couldn't open ftp: $!\n";
+            chdir $dir; return;
+        }
+
+        my @dialog = split(/\n/, <<"END_FTP");
+open $host
+user anonymous anonymous\@example.com
+cd $path
+binary
+get $file $file
+quit
+END_FTP
+        foreach (@dialog) { $fh->print("$_\n") }
+        $fh->close;
+    } }
+    else {
+        warn "No working 'ftp' program available!\n";
+        chdir $dir; return;
+    }
+
+    unless (-f $file) {
+        warn "Fetching failed: $@\n";
+        chdir $dir; return;
+    }
+
+    return if exists $args{size} and -s $file != $args{size};
+    system($args{run}) if exists $args{run};
+    unlink($file) if $args{remove};
+
+    print(((!exists $args{check_for} or -e $args{check_for})
+        ? "done!" : "failed! ($!)"), "\n");
+    chdir $dir; return !$?;
+}
+
+1;

Added: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Makefile.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Makefile.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,237 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+	shift;
+
+	# Infinite loop protection
+	my @c = caller();
+	if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+		die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+	}
+
+	# In automated testing, always use defaults
+	if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+		local $ENV{PERL_MM_USE_DEFAULT} = 1;
+		goto &ExtUtils::MakeMaker::prompt;
+	} else {
+		goto &ExtUtils::MakeMaker::prompt;
+	}
+}
+
+sub makemaker_args {
+	my $self = shift;
+	my $args = ($self->{makemaker_args} ||= {});
+	%$args = ( %$args, @_ ) if @_;
+	$args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+	my $self = sShift;
+	my $name = shift;
+	my $args = $self->makemaker_args;
+	$args->{name} = defined $args->{$name}
+		? join( ' ', $args->{name}, @_ )
+		: join( ' ', @_ );
+}
+
+sub build_subdirs {
+	my $self    = shift;
+	my $subdirs = $self->makemaker_args->{DIR} ||= [];
+	for my $subdir (@_) {
+		push @$subdirs, $subdir;
+	}
+}
+
+sub clean_files {
+	my $self  = shift;
+	my $clean = $self->makemaker_args->{clean} ||= {};
+	%$clean = (
+		%$clean, 
+		FILES => join(' ', grep length, $clean->{FILES}, @_),
+	);
+}
+
+sub realclean_files {
+	my $self  = shift;
+	my $realclean = $self->makemaker_args->{realclean} ||= {};
+	%$realclean = (
+		%$realclean, 
+		FILES => join(' ', grep length, $realclean->{FILES}, @_),
+	);
+}
+
+sub libs {
+	my $self = shift;
+	my $libs = ref $_[0] ? shift : [ shift ];
+	$self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+	my $self = shift;
+	$self->makemaker_args( INC => shift );
+}
+
+my %test_dir = ();
+
+sub _wanted_t {
+	/\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1;
+}
+
+sub tests_recursive {
+	my $self = shift;
+	if ( $self->tests ) {
+		die "tests_recursive will not work if tests are already defined";
+	}
+	my $dir = shift || 't';
+	unless ( -d $dir ) {
+		die "tests_recursive dir '$dir' does not exist";
+	}
+	require File::Find;
+	%test_dir = ();
+	File::Find::find( \&_wanted_t, $dir );
+	$self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
+}
+
+sub write {
+	my $self = shift;
+	die "&Makefile->write() takes no arguments\n" if @_;
+
+	my $args = $self->makemaker_args;
+	$args->{DISTNAME} = $self->name;
+	$args->{NAME}     = $self->module_name || $self->name || $self->determine_NAME($args);
+	$args->{VERSION}  = $self->version || $self->determine_VERSION($args);
+	$args->{NAME}     =~ s/-/::/g;
+	if ( $self->tests ) {
+		$args->{test} = { TESTS => $self->tests };
+	}
+	if ($] >= 5.005) {
+		$args->{ABSTRACT} = $self->abstract;
+		$args->{AUTHOR}   = $self->author;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+		$args->{NO_META} = 1;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+		$args->{SIGN} = 1;
+	}
+	unless ( $self->is_admin ) {
+		delete $args->{SIGN};
+	}
+
+	# merge both kinds of requires into prereq_pm
+	my $prereq = ($args->{PREREQ_PM} ||= {});
+	%$prereq = ( %$prereq,
+		map { @$_ }
+		map { @$_ }
+		grep $_,
+		($self->build_requires, $self->requires)
+	);
+
+	# merge both kinds of requires into prereq_pm
+	my $subdirs = ($args->{DIR} ||= []);
+	if ($self->bundles) {
+		foreach my $bundle (@{ $self->bundles }) {
+			my ($file, $dir) = @$bundle;
+			push @$subdirs, $dir if -d $dir;
+			delete $prereq->{$file};
+		}
+	}
+
+	if ( my $perl_version = $self->perl_version ) {
+		eval "use $perl_version; 1"
+			or die "ERROR: perl: Version $] is installed, "
+			. "but we need version >= $perl_version";
+	}
+
+	$args->{INSTALLDIRS} = $self->installdirs;
+
+	my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+
+	my $user_preop = delete $args{dist}->{PREOP};
+	if (my $preop = $self->admin->preop($user_preop)) {
+		$args{dist} = $preop;
+	}
+
+	my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+	$self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+	my $self          = shift;
+	my $makefile_name = shift;
+	my $top_class     = ref($self->_top) || '';
+	my $top_version   = $self->_top->VERSION || '';
+
+	my $preamble = $self->preamble 
+		? "# Preamble by $top_class $top_version\n"
+			. $self->preamble
+		: '';
+	my $postamble = "# Postamble by $top_class $top_version\n"
+		. ($self->postamble || '');
+
+	local *MAKEFILE;
+	open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	my $makefile = do { local $/; <MAKEFILE> };
+	close MAKEFILE or die $!;
+
+	$makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+	$makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+	$makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+	$makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+	$makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+	# Module::Install will never be used to build the Core Perl
+	# Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+	# PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+	$makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+	#$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+	# Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+	$makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
+
+	# XXX - This is currently unused; not sure if it breaks other MM-users
+	# $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+	open  MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	print MAKEFILE  "$preamble$makefile$postamble" or die $!;
+	close MAKEFILE  or die $!;
+
+	1;
+}
+
+sub preamble {
+	my ($self, $text) = @_;
+	$self->{preamble} = $text . $self->{preamble} if defined $text;
+	$self->{preamble};
+}
+
+sub postamble {
+	my ($self, $text) = @_;
+	$self->{postamble} ||= $self->admin->postamble;
+	$self->{postamble} .= $text if defined $text;
+	$self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 363

Modified: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Metadata.pm
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/inc/Module/Install/Metadata.pm	(original)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Metadata.pm	Tue Jun 12 15:48:07 2007
@@ -6,14 +6,14 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.64';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }
 
 my @scalar_keys = qw{
     name module_name abstract author version license
-    distribution_type perl_version tests
+    distribution_type perl_version tests installdirs
 };
 
 my @tuple_keys = qw{
@@ -56,9 +56,23 @@
     };
 }
 
+# configure_requires is currently a null-op
+sub configure_requires { 1 }
+
+# Aliases for build_requires that will have alternative
+# meanings in some future version of META.yml.
+sub test_requires      { shift->build_requires(@_)  }
+sub install_requires   { shift->build_requires(@_)  }
+
+# Aliases for installdirs options
+sub install_as_core    { $_[0]->installdirs('perl')   }
+sub install_as_cpan    { $_[0]->installdirs('site')   }
+sub install_as_site    { $_[0]->installdirs('site')   }
+sub install_as_vendor  { $_[0]->installdirs('vendor') }
+
 sub sign {
     my $self = shift;
-    return $self->{'values'}{'sign'} if defined wantarray and !@_;
+    return $self->{'values'}{'sign'} if defined wantarray and ! @_;
     $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
     return $self;
 }
@@ -279,9 +293,11 @@
 
     if (
         $self->_slurp($file) =~ m/
-        =head \d \s+
-        (?:licen[cs]e|licensing|copyright|legal)\b
-        (.*?)
+        (
+            =head \d \s+
+            (?:licen[cs]e|licensing|copyright|legal)\b
+            .*?
+        )
         (=head\\d.*|=cut.*|)
         \z
     /ixms
@@ -289,19 +305,24 @@
     {
         my $license_text = $1;
         my @phrases      = (
-            'under the same (?:terms|license) as perl itself' => 'perl',
-            'GNU public license'                              => 'gpl',
-            'GNU lesser public license'                       => 'gpl',
-            'BSD license'                                     => 'bsd',
-            'Artistic license'                                => 'artistic',
-            'GPL'                                             => 'gpl',
-            'LGPL'                                            => 'lgpl',
-            'BSD'                                             => 'bsd',
-            'Artistic'                                        => 'artistic',
+            'under the same (?:terms|license) as perl itself' => 'perl',        1,
+            'GNU public license'                              => 'gpl',         1,
+            'GNU lesser public license'                       => 'gpl',         1,
+            'BSD license'                                     => 'bsd',         1,
+            'Artistic license'                                => 'artistic',    1,
+            'GPL'                                             => 'gpl',         1,
+            'LGPL'                                            => 'lgpl',        1,
+            'BSD'                                             => 'bsd',         1,
+            'Artistic'                                        => 'artistic',    1,
+            'MIT'                                             => 'mit',         1,
+            'proprietary'                                     => 'proprietary', 0,
         );
-        while ( my ( $pattern, $license ) = splice( @phrases, 0, 2 ) ) {
+        while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
             $pattern =~ s{\s+}{\\s+}g;
             if ( $license_text =~ /\b$pattern\b/i ) {
+                if ( $osi and $license_text =~ /All rights reserved/i ) {
+                        warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
+		}
                 $self->license($license);
                 return 1;
             }

Added: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Win32.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/Win32.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,65 @@
+#line 1
+package Module::Install::Win32;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# determine if the user needs nmake, and download it if needed
+sub check_nmake {
+	my $self = shift;
+	$self->load('can_run');
+	$self->load('get_file');
+	
+	require Config;
+	return unless (
+		$^O eq 'MSWin32'                     and
+		$Config::Config{make}                and
+		$Config::Config{make} =~ /^nmake\b/i and
+		! $self->can_run('nmake')
+	);
+
+	print "The required 'nmake' executable not found, fetching it...\n";
+
+	require File::Basename;
+	my $rv = $self->get_file(
+		url       => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
+		ftp_url   => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
+		local_dir => File::Basename::dirname($^X),
+		size      => 51928,
+		run       => 'Nmake15.exe /o > nul',
+		check_for => 'Nmake.exe',
+		remove    => 1,
+	);
+
+	if (!$rv) {
+        die <<'END_MESSAGE';
+
+-------------------------------------------------------------------------------
+
+Since you are using Microsoft Windows, you will need the 'nmake' utility
+before installation. It's available at:
+
+  http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+      or
+  ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
+
+Please download the file manually, save it to a directory in %PATH% (e.g.
+C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
+that directory, and run "Nmake15.exe" from there; that will create the
+'nmake.exe' file needed by this module.
+
+You may then resume the installation process described in README.
+
+-------------------------------------------------------------------------------
+END_MESSAGE
+	}
+}
+
+1;

Modified: jifty/branches/virtual-models/examples/Yada/inc/Module/Install/WriteAll.pm
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/inc/Module/Install/WriteAll.pm	(original)
+++ jifty/branches/virtual-models/examples/Yada/inc/Module/Install/WriteAll.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.64';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Added: jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/Todo.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/Todo.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+
+package Yada::Model::Todo;
+use Jifty::DBI::Schema;
+
+use Yada::Record schema {
+
+    column done => type is 'bool';
+    column description => type is 'text';
+
+};
+
+# Your model-specific methods go here.
+
+1;
+

Added: jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/User.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada/Model/User.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,11 @@
+package Yada::Model::User;
+#use base 'Example::Todo::Record';
+use Jifty::DBI::Schema;
+
+use Yada::Record schema {};
+
+use Jifty::Plugin::User::Mixin::Model::User;
+use Jifty::Plugin::Authentication::Password::Mixin::Model::User;
+use Jifty::Plugin::OpenID::Mixin::Model::User;
+
+1;

Added: jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada/View.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,18 @@
+package Yada::View;
+use Jifty::View::Declare -base;
+
+template 'index.html' => page {
+    my $self = shift;
+    title { _('Yada!') };
+
+    form {
+	render_region(name => 'list', path => '/todo/list');
+    }
+};
+
+use Jifty::View::Declare::CRUD;
+for (qw/todo/) {
+    Jifty::View::Declare::CRUD->mount_view($_);
+}
+
+1;

Added: jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/examples/Yada/lib/Yada/View/Todo.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,6 @@
+package Yada::View::Todo;
+use strict;
+use Jifty::View::Declare -base;
+use base 'Jifty::View::Declare::CRUD';
+
+1;

Modified: jifty/branches/virtual-models/examples/Yada/t/00-model-Todo.t
==============================================================================
--- /jifty/branches/virtual-models/examples/Example-Todo/t/00-model-Todo.t	(original)
+++ jifty/branches/virtual-models/examples/Yada/t/00-model-Todo.t	Tue Jun 12 15:48:07 2007
@@ -11,14 +11,14 @@
 use Jifty::Test tests => 11;
 
 # Make sure we can load the model
-use_ok('Example::Todo::Model::Todo');
+use_ok('Yada::Model::Todo');
 
 # Grab a system user
-my $system_user = Example::Todo::CurrentUser->superuser;
+my $system_user = Yada::CurrentUser->superuser;
 ok($system_user, "Found a system user");
 
 # Try testing a create
-my $o = Example::Todo::Model::Todo->new(current_user => $system_user);
+my $o = Yada::Model::Todo->new(current_user => $system_user);
 my ($id) = $o->create();
 ok($id, "Todo create returned success");
 ok($o->id, "New Todo has valid id set");
@@ -30,7 +30,7 @@
 isnt($o->id, $id, "And it is different from the previous one");
 
 # Searches in general
-my $collection =  Example::Todo::Model::TodoCollection->new(current_user => $system_user);
+my $collection =  Yada::Model::TodoCollection->new(current_user => $system_user);
 $collection->unlimit;
 is($collection->count, 2, "Finds two records");
 

Modified: jifty/branches/virtual-models/inc/Module/Install.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install.pm	Tue Jun 12 15:48:07 2007
@@ -28,7 +28,7 @@
     # This is not enforced yet, but will be some time in the next few
     # releases once we can make sure it won't clash with custom
     # Module::Install extensions.
-    $VERSION = '0.65';
+    $VERSION = '0.67';
 }
 
 # Whether or not inc::Module::Install is actually loaded, the
@@ -86,7 +86,7 @@
             # delegate back to parent dirs
             goto &$code unless $cwd eq $pwd;
         }
-        $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+        $$sym =~ /([^:]+)$/ or Carp::confess "Cannot autoload $who - $sym";
         unshift @_, ($self, $1);
         goto &{$self->can('call')} unless uc($1) eq $1;
     };

Modified: jifty/branches/virtual-models/inc/Module/Install/AutoInstall.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/AutoInstall.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/AutoInstall.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/Base.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Base.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Base.pm	Tue Jun 12 15:48:07 2007
@@ -1,7 +1,7 @@
 #line 1
 package Module::Install::Base;
 
-$VERSION = '0.65';
+$VERSION = '0.67';
 
 # Suspend handler for "redefined" warnings
 BEGIN {

Modified: jifty/branches/virtual-models/inc/Module/Install/Can.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Can.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Can.pm	Tue Jun 12 15:48:07 2007
@@ -11,7 +11,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/Fetch.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Fetch.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Fetch.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/Include.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Include.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Include.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/Makefile.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Makefile.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Makefile.pm	Tue Jun 12 15:48:07 2007
@@ -7,7 +7,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }
@@ -17,196 +17,221 @@
 my %seen = ();
 
 sub prompt {
-    shift;
+	shift;
 
-    # Infinite loop protection
-    my @c = caller();
-    if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
-        die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
-    }
-
-    # In automated testing, always use defaults
-    if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
-        local $ENV{PERL_MM_USE_DEFAULT} = 1;
-        goto &ExtUtils::MakeMaker::prompt;
-    } else {
-        goto &ExtUtils::MakeMaker::prompt;
-    }
+	# Infinite loop protection
+	my @c = caller();
+	if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+		die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+	}
+
+	# In automated testing, always use defaults
+	if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+		local $ENV{PERL_MM_USE_DEFAULT} = 1;
+		goto &ExtUtils::MakeMaker::prompt;
+	} else {
+		goto &ExtUtils::MakeMaker::prompt;
+	}
 }
 
 sub makemaker_args {
-    my $self = shift;
-    my $args = ($self->{makemaker_args} ||= {});
-    %$args = ( %$args, @_ ) if @_;
-    $args;
+	my $self = shift;
+	my $args = ($self->{makemaker_args} ||= {});
+	%$args = ( %$args, @_ ) if @_;
+	$args;
 }
 
 # For mm args that take multiple space-seperated args,
 # append an argument to the current list.
 sub makemaker_append {
-    my $self = shift;
-    my $name = shift;
-    my $args = $self->makemaker_args;
-    $args->{name} = defined $args->{$name}
-    	? join( ' ', $args->{name}, @_ )
-    	: join( ' ', @_ );
+	my $self = sShift;
+	my $name = shift;
+	my $args = $self->makemaker_args;
+	$args->{name} = defined $args->{$name}
+		? join( ' ', $args->{name}, @_ )
+		: join( ' ', @_ );
 }
 
 sub build_subdirs {
-    my $self    = shift;
-    my $subdirs = $self->makemaker_args->{DIR} ||= [];
-    for my $subdir (@_) {
-        push @$subdirs, $subdir;
-    }
+	my $self    = shift;
+	my $subdirs = $self->makemaker_args->{DIR} ||= [];
+	for my $subdir (@_) {
+		push @$subdirs, $subdir;
+	}
 }
 
 sub clean_files {
-    my $self  = shift;
-    my $clean = $self->makemaker_args->{clean} ||= {};
-    %$clean = (
-        %$clean, 
-        FILES => join(' ', grep length, $clean->{FILES}, @_),
-    );
+	my $self  = shift;
+	my $clean = $self->makemaker_args->{clean} ||= {};
+	%$clean = (
+		%$clean, 
+		FILES => join(' ', grep length, $clean->{FILES}, @_),
+	);
 }
 
 sub realclean_files {
-    my $self  = shift;
-    my $realclean = $self->makemaker_args->{realclean} ||= {};
-    %$realclean = (
-        %$realclean, 
-        FILES => join(' ', grep length, $realclean->{FILES}, @_),
-    );
+	my $self  = shift;
+	my $realclean = $self->makemaker_args->{realclean} ||= {};
+	%$realclean = (
+		%$realclean, 
+		FILES => join(' ', grep length, $realclean->{FILES}, @_),
+	);
 }
 
 sub libs {
-    my $self = shift;
-    my $libs = ref $_[0] ? shift : [ shift ];
-    $self->makemaker_args( LIBS => $libs );
+	my $self = shift;
+	my $libs = ref $_[0] ? shift : [ shift ];
+	$self->makemaker_args( LIBS => $libs );
 }
 
 sub inc {
-    my $self = shift;
-    $self->makemaker_args( INC => shift );
+	my $self = shift;
+	$self->makemaker_args( INC => shift );
+}
+
+my %test_dir = ();
+
+sub _wanted_t {
+	/\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1;
+}
+
+sub tests_recursive {
+	my $self = shift;
+	if ( $self->tests ) {
+		die "tests_recursive will not work if tests are already defined";
+	}
+	my $dir = shift || 't';
+	unless ( -d $dir ) {
+		die "tests_recursive dir '$dir' does not exist";
+	}
+	require File::Find;
+	%test_dir = ();
+	File::Find::find( \&_wanted_t, $dir );
+	$self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
 }
 
 sub write {
-    my $self = shift;
-    die "&Makefile->write() takes no arguments\n" if @_;
+	my $self = shift;
+	die "&Makefile->write() takes no arguments\n" if @_;
 
-    my $args = $self->makemaker_args;
-    $args->{DISTNAME} = $self->name;
-    $args->{NAME}     = $self->module_name || $self->name || $self->determine_NAME($args);
-    $args->{VERSION}  = $self->version || $self->determine_VERSION($args);
-    $args->{NAME}     =~ s/-/::/g;
-    if ( $self->tests ) {
-        $args->{test} = { TESTS => $self->tests };
-    }
-    if ($] >= 5.005) {
-        $args->{ABSTRACT} = $self->abstract;
-        $args->{AUTHOR}   = $self->author;
-    }
-    if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
-        $args->{NO_META} = 1;
-    }
-    if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
-        $args->{SIGN} = 1;
-    }
-    unless ( $self->is_admin ) {
-        delete $args->{SIGN};
-    }
-
-    # merge both kinds of requires into prereq_pm
-    my $prereq = ($args->{PREREQ_PM} ||= {});
-    %$prereq = ( %$prereq, map { @$_ } map { @$_ } grep $_,
-                 ($self->build_requires, $self->requires) );
-
-    # merge both kinds of requires into prereq_pm
-    my $subdirs = ($args->{DIR} ||= []);
-    if ($self->bundles) {
-        foreach my $bundle (@{ $self->bundles }) {
-            my ($file, $dir) = @$bundle;
-            push @$subdirs, $dir if -d $dir;
-            delete $prereq->{$file};
-        }
-    }
-
-    if ( my $perl_version = $self->perl_version ) {
-        eval "use $perl_version; 1"
-            or die "ERROR: perl: Version $] is installed, "
-                . "but we need version >= $perl_version";
-    }
-
-    $args->{INSTALLDIRS} = $self->installdirs;
-
-    my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
-
-    my $user_preop = delete $args{dist}->{PREOP};
-    if (my $preop = $self->admin->preop($user_preop)) {
-        $args{dist} = $preop;
-    }
+	my $args = $self->makemaker_args;
+	$args->{DISTNAME} = $self->name;
+	$args->{NAME}     = $self->module_name || $self->name || $self->determine_NAME($args);
+	$args->{VERSION}  = $self->version || $self->determine_VERSION($args);
+	$args->{NAME}     =~ s/-/::/g;
+	if ( $self->tests ) {
+		$args->{test} = { TESTS => $self->tests };
+	}
+	if ($] >= 5.005) {
+		$args->{ABSTRACT} = $self->abstract;
+		$args->{AUTHOR}   = $self->author;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+		$args->{NO_META} = 1;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+		$args->{SIGN} = 1;
+	}
+	unless ( $self->is_admin ) {
+		delete $args->{SIGN};
+	}
+
+	# merge both kinds of requires into prereq_pm
+	my $prereq = ($args->{PREREQ_PM} ||= {});
+	%$prereq = ( %$prereq,
+		map { @$_ }
+		map { @$_ }
+		grep $_,
+		($self->build_requires, $self->requires)
+	);
+
+	# merge both kinds of requires into prereq_pm
+	my $subdirs = ($args->{DIR} ||= []);
+	if ($self->bundles) {
+		foreach my $bundle (@{ $self->bundles }) {
+			my ($file, $dir) = @$bundle;
+			push @$subdirs, $dir if -d $dir;
+			delete $prereq->{$file};
+		}
+	}
+
+	if ( my $perl_version = $self->perl_version ) {
+		eval "use $perl_version; 1"
+			or die "ERROR: perl: Version $] is installed, "
+			. "but we need version >= $perl_version";
+	}
+
+	$args->{INSTALLDIRS} = $self->installdirs;
+
+	my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+
+	my $user_preop = delete $args{dist}->{PREOP};
+	if (my $preop = $self->admin->preop($user_preop)) {
+		$args{dist} = $preop;
+	}
 
-    my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
-    $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+	my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+	$self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
 }
 
 sub fix_up_makefile {
-    my $self          = shift;
-    my $makefile_name = shift;
-    my $top_class     = ref($self->_top) || '';
-    my $top_version   = $self->_top->VERSION || '';
-
-    my $preamble = $self->preamble 
-        ? "# Preamble by $top_class $top_version\n"
-            . $self->preamble
-        : '';
-    my $postamble = "# Postamble by $top_class $top_version\n"
-        . ($self->postamble || '');
-
-    local *MAKEFILE;
-    open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
-    my $makefile = do { local $/; <MAKEFILE> };
-    close MAKEFILE or die $!;
-
-    $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
-    $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
-    $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
-    $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
-    $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
-
-    # Module::Install will never be used to build the Core Perl
-    # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
-    # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
-    $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
-    #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
-
-    # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
-    $makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
-
-    # XXX - This is currently unused; not sure if it breaks other MM-users
-    # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
-
-    open  MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
-    print MAKEFILE  "$preamble$makefile$postamble" or die $!;
-    close MAKEFILE  or die $!;
+	my $self          = shift;
+	my $makefile_name = shift;
+	my $top_class     = ref($self->_top) || '';
+	my $top_version   = $self->_top->VERSION || '';
+
+	my $preamble = $self->preamble 
+		? "# Preamble by $top_class $top_version\n"
+			. $self->preamble
+		: '';
+	my $postamble = "# Postamble by $top_class $top_version\n"
+		. ($self->postamble || '');
+
+	local *MAKEFILE;
+	open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	my $makefile = do { local $/; <MAKEFILE> };
+	close MAKEFILE or die $!;
+
+	$makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+	$makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+	$makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+	$makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+	$makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+	# Module::Install will never be used to build the Core Perl
+	# Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+	# PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+	$makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+	#$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+	# Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+	$makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
+
+	# XXX - This is currently unused; not sure if it breaks other MM-users
+	# $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+	open  MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	print MAKEFILE  "$preamble$makefile$postamble" or die $!;
+	close MAKEFILE  or die $!;
 
-    1;
+	1;
 }
 
 sub preamble {
-    my ($self, $text) = @_;
-    $self->{preamble} = $text . $self->{preamble} if defined $text;
-    $self->{preamble};
+	my ($self, $text) = @_;
+	$self->{preamble} = $text . $self->{preamble} if defined $text;
+	$self->{preamble};
 }
 
 sub postamble {
-    my ($self, $text) = @_;
-    $self->{postamble} ||= $self->admin->postamble;
-    $self->{postamble} .= $text if defined $text;
-    $self->{postamble}
+	my ($self, $text) = @_;
+	$self->{postamble} ||= $self->admin->postamble;
+	$self->{postamble} .= $text if defined $text;
+	$self->{postamble}
 }
 
 1;
 
 __END__
 
-#line 338
+#line 363

Modified: jifty/branches/virtual-models/inc/Module/Install/Metadata.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Metadata.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Metadata.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }
@@ -56,14 +56,23 @@
     };
 }
 
-sub install_as_core   { $_[0]->installdirs('perl')   }
-sub install_as_cpan   { $_[0]->installdirs('site')   }
-sub install_as_site   { $_[0]->installdirs('site')   }
-sub install_as_vendor { $_[0]->installdirs('vendor') }
+# configure_requires is currently a null-op
+sub configure_requires { 1 }
+
+# Aliases for build_requires that will have alternative
+# meanings in some future version of META.yml.
+sub test_requires      { shift->build_requires(@_)  }
+sub install_requires   { shift->build_requires(@_)  }
+
+# Aliases for installdirs options
+sub install_as_core    { $_[0]->installdirs('perl')   }
+sub install_as_cpan    { $_[0]->installdirs('site')   }
+sub install_as_site    { $_[0]->installdirs('site')   }
+sub install_as_vendor  { $_[0]->installdirs('vendor') }
 
 sub sign {
     my $self = shift;
-    return $self->{'values'}{'sign'} if defined wantarray and !@_;
+    return $self->{'values'}{'sign'} if defined wantarray and ! @_;
     $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
     return $self;
 }
@@ -296,20 +305,24 @@
     {
         my $license_text = $1;
         my @phrases      = (
-            'under the same (?:terms|license) as perl itself' => 'perl',
-            'GNU public license'                              => 'gpl',
-            'GNU lesser public license'                       => 'gpl',
-            'BSD license'                                     => 'bsd',
-            'Artistic license'                                => 'artistic',
-            'GPL'                                             => 'gpl',
-            'LGPL'                                            => 'lgpl',
-            'BSD'                                             => 'bsd',
-            'Artistic'                                        => 'artistic',
-            'MIT'                                             => 'MIT',
+            'under the same (?:terms|license) as perl itself' => 'perl',        1,
+            'GNU public license'                              => 'gpl',         1,
+            'GNU lesser public license'                       => 'gpl',         1,
+            'BSD license'                                     => 'bsd',         1,
+            'Artistic license'                                => 'artistic',    1,
+            'GPL'                                             => 'gpl',         1,
+            'LGPL'                                            => 'lgpl',        1,
+            'BSD'                                             => 'bsd',         1,
+            'Artistic'                                        => 'artistic',    1,
+            'MIT'                                             => 'mit',         1,
+            'proprietary'                                     => 'proprietary', 0,
         );
-        while ( my ( $pattern, $license ) = splice( @phrases, 0, 2 ) ) {
+        while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
             $pattern =~ s{\s+}{\\s+}g;
             if ( $license_text =~ /\b$pattern\b/i ) {
+                if ( $osi and $license_text =~ /All rights reserved/i ) {
+                        warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
+		}
                 $self->license($license);
                 return 1;
             }

Modified: jifty/branches/virtual-models/inc/Module/Install/Scripts.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Scripts.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Scripts.pm	Tue Jun 12 15:48:07 2007
@@ -7,7 +7,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/Share.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Share.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Share.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/Win32.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/Win32.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/Win32.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/inc/Module/Install/WriteAll.pm
==============================================================================
--- jifty/branches/virtual-models/inc/Module/Install/WriteAll.pm	(original)
+++ jifty/branches/virtual-models/inc/Module/Install/WriteAll.pm	Tue Jun 12 15:48:07 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.65';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Modified: jifty/branches/virtual-models/lib/Jifty.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty.pm	Tue Jun 12 15:48:07 2007
@@ -150,8 +150,8 @@
     );
 
 
-    # Turn on logging as soon as we possibly can.
-    Jifty->logger( Jifty::Logger->new( $args{'logger_component'} ) );
+    # Add the appliation's library path
+    push @INC, File::Spec->catdir(Jifty::Util->app_root, "lib");
 
     # Create the Jifty classloader
     Jifty::ClassLoader->new( base => 'Jifty' );
@@ -377,6 +377,18 @@
     return @PLUGINS;
 }
 
+=head2 find_plugin
+
+Find plugins by name.
+
+=cut
+
+sub find_plugin {
+    my $self = shift;
+    my $name = shift;
+    return grep { $_->isa($name) } Jifty->plugins;
+}
+
 =head2 class_loader
 
 An accessor for the L<Jifty::ClassLoader> object that stores the loaded

Modified: jifty/branches/virtual-models/lib/Jifty/API.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/API.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/API.pm	Tue Jun 12 15:48:07 2007
@@ -24,7 +24,7 @@
  my @actions = Jifty->api->actions;
 
  # Check to see if an action is allowed
- if (Jifty->api->is_allow('TrueFooBar')) {
+ if (Jifty->api->is_allowed('TrueFooBar')) {
      # do something...
  }
 
@@ -204,7 +204,7 @@
 
 =head2 is_allowed CLASS
 
-Returns false if the I<CLASS> name (which is fully qualified if it is
+Returns true 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.
 

Modified: jifty/branches/virtual-models/lib/Jifty/Action.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Action.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Action.pm	Tue Jun 12 15:48:07 2007
@@ -1105,6 +1105,27 @@
 
 }
 
+=head2 deny REASON
+
+When access to an action is denied by L<Jifty::API::is_allowed> 
+the request handler calls this with a message.
+
+This should mark the action as failed and store the message
+but may also want to do other things (such as providing a nicer message
+or logging somewhere other than the jifty logs)
+
+=cut
+
+sub deny {
+    my $self = shift;
+    my $message = shift||'';
+
+    $self->result->failure(1);
+    $self->result->message($message);
+
+    return;
+}
+
 =head2 autogenerated
 
 Autogenerated Actions will always return true when this method is called. 

Modified: jifty/branches/virtual-models/lib/Jifty/Action/Record.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Action/Record.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Action/Record.pm	Tue Jun 12 15:48:07 2007
@@ -168,7 +168,7 @@
             $render_as = defined $render_as ? lc($render_as) : '';
 
             if ( defined (my $valid_values = $column->valid_values)) {
-                $info->{valid_values} = [ @$valid_values ];
+                $info->{valid_values} = $valid_values;
                 $info->{render_as}    = 'Select';
             } elsif ( defined $column->type && $column->type =~ /^bool/i ) {
                 $info->{render_as} = 'Checkbox';
@@ -217,7 +217,13 @@
                     # No need to generate arguments for
                     # JDBI::Collections, as we can't do anything
                     # useful with them yet, anyways.
-                    next;
+
+                    # However, if the column comes with a
+                    # "render_as", we can assume that the app
+                    # developer know what he/she is doing.
+                    # So we just render it as whatever specified.
+
+                    next unless $render_as;
                 }
             }
 

Modified: jifty/branches/virtual-models/lib/Jifty/ClassLoader.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/ClassLoader.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/ClassLoader.pm	Tue Jun 12 15:48:07 2007
@@ -131,7 +131,7 @@
                   "package $module;\n"
                 . "use base qw/Jifty::$1/; sub _autogenerated { 1 };\n"
             );
-    } elsif ( $module =~ /^(?:$base)::View/ ) {
+    } elsif ( $module =~ /^(?:$base)::View$/ ) {
         return $self->return_class(
                   "package $module;\n"
                 . "use Jifty::View::Declare -base; sub _autogenerated { 1 };\n"
@@ -163,7 +163,7 @@
                         (Create|Update|Delete|Search)([^\.]+)$/x ) {
         my $modelclass = $base . "::Model::" . $2;
 
-        Jifty::Util->require($modelclass);
+        Jifty::Util->_require( module => $modelclass, quiet => 1);
 
         local $@;
             eval { $modelclass->table } ;
@@ -179,10 +179,10 @@
 
     }
 
-    # This is if, not elsif because we might have $base::Action::Deleteblah 
+    # This is if, not elsif because we might have $base::Action::Deleteblah
     # that matches that last elsif clause but loses on the eval.
     if ( $module =~ /^(?:$base)::(Action|Notification)::(.*)$/x and not grep {$_ eq $base} map {ref} Jifty->plugins ) {
-        my $type = $1; 
+        my $type = $1;
         my $item = $2;
         # If we don't have the action in our own app, let's try the plugins
         # the app has loaded.
@@ -234,15 +234,15 @@
 
 sub require {
     my $self = shift;
-    
+
     my $base = $self->{base};
     # if we don't even have an application class, this trick will not work
-    return unless ($base); 
+    return unless ($base);
     Jifty::Util->require($base);
     Jifty::Util->require($base."::CurrentUser");
 
     my %models;
-    
+
 
     Jifty::Module::Pluggable->import(
         # $base goes last so we pull in the view class AFTER the model classes
@@ -256,7 +256,7 @@
     for my $full (sort keys %models) {
         $self->_require_model_related_classes($full);
     }
-        
+
 }
 
 sub _require_model_related_classes {
@@ -278,13 +278,13 @@
 
 Jifty supports model classes that aren't files on disk but instead records
 in your database. It's a little bit mind bending, but basically, you can
-build an application entirely out of the database without ever writing a 
-line of code(*). 
+build an application entirely out of the database without ever writing a
+line of code(*).
 
-* As of early 2007, this forward looking statement is mostly a lie. But we're 
+* As of early 2007, this forward looking statement is mostly a lie. But we're
 working on it.
 
-This method finds all database-backed models and instantiates jifty classes for 
+This method finds all database-backed models and instantiates jifty classes for
 them it returns a list of classnames of the models it created.
 
 =cut
@@ -311,10 +311,10 @@
 
 sub require_views {
     my $self = shift;
-    
+
     my $base = $self->{base};
     # if we don't even have an application class, this trick will not work
-    return unless ($base); 
+    return unless ($base);
     Jifty::Util->require($base."::View");
 }
 

Modified: jifty/branches/virtual-models/lib/Jifty/Config.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Config.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Config.pm	Tue Jun 12 15:48:07 2007
@@ -155,6 +155,9 @@
     # getting stuck in a default config file for an app
     $self->stash( Hash::Merge::merge( $self->defaults, $self->stash));
 
+    $self->stash($self->update_config($self->stash));
+
+
     # Finally, check for global postload hooks (these are used by the
     # test harness)
     $self->$Jifty::Config::postload()
@@ -265,16 +268,6 @@
             L10N       => {
                 PoDir => "share/po",
             },
-            Plugins => [
-            #  { LetMe               => {}, },
-                { SkeletonApp            => {}, },
-                { REST               => {}, },
-                { Halo               => {}, },
-                { ErrorTemplates     => {}, },
-                { OnlineDocs         => {}, },
-                { CompressedCSSandJS => {}, },
-                { AdminUI            => {}, }
-                ],
             Web        => {
                 Port => '8888',
                 BaseURL => 'http://localhost',
@@ -298,6 +291,62 @@
 }
 
 
+=head2 initial_config
+
+Returns a default guessed config for a new application
+
+=cut
+
+sub initial_config {
+    my $self = shift;
+    my $guess = $self->guess(@_);
+    $guess->{'framework'}->{'ConfigFileVersion'} = 2;
+
+    # These are the plugins which new apps will get by default
+            $guess->{'framework'}->{'Plugins'} = [
+              { LetMe               => {}, },
+                { SkeletonApp            => {}, },
+                { REST               => {}, },
+                { Halo               => {}, },
+                { ErrorTemplates     => {}, },
+                { OnlineDocs         => {}, },
+                { CompressedCSSandJS => {}, },
+                { AdminUI            => {}, }
+                ];
+    return $guess;
+}
+
+
+
+=head2 update_config  $CONFIG
+
+Takes an application's configuration as a hashref.  Right now, it just sets up
+plugins that match an older jifty version's defaults
+
+=cut
+
+sub update_config {
+    my $self = shift;
+    my $config = shift;
+    if ( $config->{'framework'}->{'ConfigFileVersion'} <2) {
+            # These are the plugins which old apps expect because their
+            # features used to be in the core.
+            unshift (@{$config->{'framework'}->{'Plugins'}}, 
+                { SkeletonApp            => {}, },
+                { REST               => {}, },
+                { Halo               => {}, },
+                { ErrorTemplates     => {}, },
+                { OnlineDocs         => {}, },
+                { CompressedCSSandJS => {}, },
+                { AdminUI            => {}, }
+            );
+    }
+
+    return $config;
+}
+
+
+
 =head2 defaults
 
 We have a couple default values that shouldn't be included in the
@@ -310,6 +359,7 @@
     my $self = shift;
     return {
         framework => {
+            ConfigFileVersion => '1',
             L10N => {
                 DefaultPoDir => Jifty::Util->share_root . '/po',
             },

Modified: jifty/branches/virtual-models/lib/Jifty/Continuation.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Continuation.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Continuation.pm	Tue Jun 12 15:48:07 2007
@@ -144,7 +144,7 @@
 =head2 call
 
 Call the continuation; this is generally done during request
-processing, after an actions have been run.
+processing, after actions have been run.
 L<Jifty::Request::Mapper>-controlled values are filled into the stored
 request based on the current request and response.  During the
 process, another continuation is created, with the filled-in results
@@ -156,6 +156,12 @@
 sub call {
     my $self = shift;
 
+    Jifty->log->debug("Redirect to @{[$self->request->path]} via continuation");
+    if (Jifty->web->request->argument('_webservice_redirect')) {
+	# for continuation - perform internal redirect under webservices.
+        Jifty->web->webservices_redirect($self->request->path);
+	return;
+    }
     # If we needed to fix up the path (it contains invalid
     # characters) then warn, because this may cause infinite
     # redirects
@@ -189,6 +195,7 @@
 
     # Redirect to right page if we're not there already
     Jifty->web->_redirect($next->request->path . "?J:RETURN=" . $next->id);
+    return 1;
 }
 
 =head2 return

Modified: jifty/branches/virtual-models/lib/Jifty/CurrentUser.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/CurrentUser.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/CurrentUser.pm	Tue Jun 12 15:48:07 2007
@@ -160,7 +160,7 @@
 sub username {
     my $self = shift;
     return undef unless ($self->user_object);
-    return($self->user_object->username(@_));
+    return($self->user_object->brief_description(@_));
 }
 
 =head2 auth_token

Modified: jifty/branches/virtual-models/lib/Jifty/DateTime.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/DateTime.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/DateTime.pm	Tue Jun 12 15:48:07 2007
@@ -16,6 +16,15 @@
 
 =cut
 
+BEGIN {
+    # we spent about 30% of the time in validate during 'require
+    # DateTime::Locale' which isn't necessary at all
+    require Params::Validate;
+    no warnings 'redefine';
+    local *Params::Validate::validate = sub { pop @_, return @_ };
+    require DateTime::Locale;
+}
+
 use base qw(Jifty::Object DateTime);
 
 

Modified: jifty/branches/virtual-models/lib/Jifty/Dispatcher.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Dispatcher.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Dispatcher.pm	Tue Jun 12 15:48:07 2007
@@ -1149,6 +1149,7 @@
     my $self     = shift;
     my $template = shift;
     my $showed   = 0;
+#    local $@;
     eval {
         foreach my $handler ( Jifty->handler->view_handlers ) {
             if ( Jifty->handler->view($handler)->template_exists($template) ) {
@@ -1166,8 +1167,13 @@
     my $err = $@;
 
     # Handle parse errors
+    $self->log->fatal("view class error: $err") if $err;
     if ( $err and not eval { $err->isa('HTML::Mason::Exception::Abort') } ) {
-
+        if ($template eq '/__jifty/error/mason_internal_error') {
+            $self->log->debug("can't render internal_error: $err");
+            $self->_abort;
+            return;
+        }
         # Save the request away, and redirect to an error page
         Jifty->web->response->error($err);
         my $c = Jifty::Continuation->new(
@@ -1176,8 +1182,6 @@
             parent   => Jifty->web->request->continuation,
         );
 
-        warn "$err";
-
         # Redirect with a continuation
         Jifty->web->_redirect( "/__jifty/error/mason_internal_error?J:C=" . $c->id );
     } elsif ($err) {

Modified: jifty/branches/virtual-models/lib/Jifty/I18N.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/I18N.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/I18N.pm	Tue Jun 12 15:48:07 2007
@@ -44,12 +44,20 @@
 
 my $DynamicLH;
 
+our $loaded;
+
 sub new {
     my $class = shift;
     my $self  = {};
     bless $self, $class;
 
+    # XXX: this requires a full review, LML->get_handle is calling new
+    # on I18N::lang each time, but we really shouldn't need to rerun
+    # the import here.
+    return $self if $loaded;
+
     my @import = map {( Gettext => $_ )} _get_file_patterns();
+    ++$loaded;
 
     Locale::Maketext::Lexicon->import(
         {   '*' => \@import,

Modified: jifty/branches/virtual-models/lib/Jifty/LetMe.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/LetMe.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/LetMe.pm	Tue Jun 12 15:48:07 2007
@@ -84,7 +84,7 @@
     my $currentuser_object_class = Jifty->app_class("CurrentUser");
     my $u = $currentuser_object_class->new( email => $email )->user_object;
     # we want to be able to get at their auth token.
-    $u->current_user(Jifty->app_class('CurrentUser')->superuser);
+    $u->current_user( $currentuser_object_class->superuser );
     return $u;
 }
 

Modified: jifty/branches/virtual-models/lib/Jifty/Manual/Continuations.pod
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Manual/Continuations.pod	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Manual/Continuations.pod	Tue Jun 12 15:48:07 2007
@@ -171,7 +171,7 @@
 exactly like rendered-as-links tangents, in that when they return,
 I<all> rules in the dispatcher are still executed from the start.
 Therefore the C<unless> guard in the C<before '/protected'> rule above
-is neccessary to prevent recursion.
+is necessary to prevent recursion.
 
 =head1 GORY DETAILS
 

Modified: jifty/branches/virtual-models/lib/Jifty/Manual/Cookbook.pod
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Manual/Cookbook.pod	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Manual/Cookbook.pod	Tue Jun 12 15:48:07 2007
@@ -177,7 +177,7 @@
 
     before qr'^/secret' => run {
         unless(Jifty->web->current_user->id) {
-            Jifty->web->tangent('/login');
+            Jifty->web->tangent(url => '/login');
         }
     };
 
@@ -402,3 +402,28 @@
 
 =for comment
 Document how to do this with Mason
+
+=head2 Show login box on an action submit
+
+In app's dispatcher add the following:
+
+    before '*' => run {
+        # do nothing if user is logged in
+        return if Jifty->web->current_user->id;
+
+        # check all actions the request has. if at least one require login
+        # then save them in a continuation and redirect to the login page
+        tangent '/login' if
+            grep $_->can('require_login') && $_->require_login,
+            map $_->class, Jifty->web->request->actions;
+    };
+
+All you have to do now is to add C<sub require_login { return 1 }> into
+actions which need this functionality.
+
+Note that you can implement complex logic in the require_login method,
+but it's called as class method what set a lot of limitations. That
+would be really cool to have access to all data of the action in
+this method, so you are welcome to post a better solution.
+
+=cut

Modified: jifty/branches/virtual-models/lib/Jifty/Manual/Models.pod
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Manual/Models.pod	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Manual/Models.pod	Tue Jun 12 15:48:07 2007
@@ -94,7 +94,7 @@
        | 'validator' 'is' subroutine_reference
        | 'immutable'
        | 'unreadable'
-       | 'length' 'is' number
+       | 'max_length' 'is' number
        | 'mandatory'
        | 'not_null'
        | 'distinct'

Modified: jifty/branches/virtual-models/lib/Jifty/Module/Pluggable.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Module/Pluggable.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Module/Pluggable.pm	Tue Jun 12 15:48:07 2007
@@ -21,15 +21,15 @@
     To:       jesse at bestpractical.com
 
 On Mon, Oct 23, 2006 at 09:11:22PM -0700, Jesse Vincent said:
-does this thread make any sense to you? It looks like  
+does this thread make any sense to you? It looks like
 Module::Pluggable is interacting poorly with UNIVERSAL::require?
 
-Module::Pluggable used to to use UNIVERSAL::require but I switched 
+Module::Pluggable used to to use UNIVERSAL::require but I switched
 because I was trying to get rid of dependencies.
 
-I farmed the requiring stuff off to it's own _require method in order to 
-make it easy to subclass so that people could ovveride how the require 
-was done. 
+I farmed the requiring stuff off to it's own _require method in order to
+make it easy to subclass so that people could ovveride how the require
+was done.
 
 
 =head3  But Simon is lying...
@@ -51,17 +51,19 @@
     # Module::Pluggable::Object::_require expects a true value (the error message) on failure
     # On success, it expects you to return undef.
 
+    return if $module =~/\.#/;
+
     local $UNIVERSAL::require::ERROR;
 
     no warnings; # This is lexical and turns off exactly one warning below -- "Can't locate package in @ISA".
                  # (for some reason, "no warnings 'syntax'" does not work as advertised here.)
                  # Note that it does _not_ turn off warnings triggered in the $module itself.
     if ((not $module->require()) &&  ( $UNIVERSAL::require::ERROR !~ /^Can't locate/)) {
-        die $UNIVERSAL::require::ERROR;    
-    } 
+        die $UNIVERSAL::require::ERROR;
+    }
          # We'd prefer to use Jifty::Util->require() here, but it spews crazy warnings
 
     return $UNIVERSAL::require::ERROR;
-} 
+}
 
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm	Tue Jun 12 15:48:07 2007
@@ -45,9 +45,7 @@
 sub setup {
     my $self = shift;
     my $LoginUser   = Jifty->app_class('Model', 'User');
-        my $CurrentUser   = Jifty->app_class('CurrentUser');
-
-
+    my $CurrentUser = Jifty->app_class('CurrentUser');
 
     $self->user_object(
         $LoginUser->new( current_user => $CurrentUser->superuser ) );
@@ -63,9 +61,8 @@
     my $self  = shift;
     my $email = shift;
 
-    my $LoginUser   = "Jifty::Plugin::Authentication::Password::Model::User";
-        my $CurrentUser = "Jifty::Plugin::Authentication::Password::CurrentUser";
-
+    my $LoginUser   = Jifty->app_class('Model', 'User');
+    my $CurrentUser = Jifty->app_class('CurrentUser');
 
     return $self->validation_error(
         address => _("That doesn't look like an email address.") )

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm	Tue Jun 12 15:48:07 2007
@@ -7,7 +7,7 @@
 
 use Digest::MD5 qw'';
 
-our @EXPORT = qw(password_is hashed_password_is regenerate_auth_token);
+our @EXPORT = qw(password_is hashed_password_is regenerate_auth_token has_alternative_auth);
 
 use Jifty::Plugin::Authentication::Password::Record schema {
 
@@ -21,7 +21,6 @@
 
 
 column password =>
-  is mandatory,
   is unreadable,
   label is _('Password'),
   type is 'varchar',
@@ -77,7 +76,8 @@
 
 =head2 validate_password
 
-Makes sure that the password is six characters long or longer.
+Makes sure that the password is six characters long or longer, unless
+we have alternative means to authenticate.
 
 =cut
 
@@ -85,6 +85,8 @@
     my $self      = shift;
     my $new_value = shift;
 
+    return 1 if $self->has_alternative_auth();
+
     return ( 0, _('Passwords need to be at least six characters long') )
         if length($new_value) < 6;
 
@@ -102,9 +104,17 @@
     } else {
         warn  $self->id . " " .$self->email;
     }
+}
 
+=head2 has_alternative_auth
 
-}
+If your model supports other means of authentication, you should have
+this method return true, so the C<password> field can optionally be
+null and authentication with password is disabled in that case.
+
+=cut
+
+sub has_alternative_auth { }
 
 =head2 after_set_password
 

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/View.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/View.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Authentication/Password/View.pm	Tue Jun 12 15:48:07 2007
@@ -14,14 +14,13 @@
 =cut
 
 package Jifty::Plugin::Authentication::Password::View;
-use HTML::Entities ();
 use Jifty::View::Declare -base;
 
 { no warnings 'redefine';
 sub page (&) {
     no strict 'refs'; 
     BEGIN {Jifty::Util->require(Jifty->app_class('View'))};
-    &{Jifty->app_class('View') . "::page"}(@_);
+    Jifty->app_class('View')->can('page')->(@_);
 }
 }
 
@@ -119,4 +118,35 @@
     Jifty->web->form->end();
 };
 
+template 'resend_confirmation' => page {
+    attr { title => "Resend Confirmation Email" };
+    my $resend = Jifty->web->new_action(
+        class   => 'ResendConfirmation',
+        moniker => 'resendconf'
+    );
+
+    if (    Jifty->web->current_user->id
+        and Jifty->web->current_user->user_object->email_confirmed )
+    {
+        Jifty->web->redirect('/');
+    } else {
+        div {
+            attr { id => 'overview' };
+            form {
+                Jifty->web->form->next_page( url => '/' );
+
+                p {
+                    _(  q{Fill in your address below, and we'll send out another confirmation email to you. }
+                    );
+                    render_param( $resend => 'email', focus => 1 );
+                    form_submit( label => 'Resend Confirmation' );
+                    }
+                }
+            }
+
+    }
+};
+
+
+
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS.pm	Tue Jun 12 15:48:07 2007
@@ -2,20 +2,187 @@
 use warnings;
 
 package Jifty::Plugin::CompressedCSSandJS;
-use base qw/Jifty::Plugin/;
+use base qw/Jifty::Plugin Class::Accessor::Fast/;
 
-# Your plugin goes here.  If takes any configuration or arguments, you
-# probably want to override L<Jifty::Plugin/init>.
+use Digest::MD5 qw(md5_hex);
+use IPC::Run3 'run3';
+use IO::Handle ();
 
 =head1 NAME
 
 Jifty::Plugin::CompressedCSSandJS
 
+=head1 SYNOPSIS
+
+# In your jifty config.yml under the framework section:
+
+  Plugins:
+    - CompressedCSSandJS:
+        js: 1
+        css: 1
+        jsmin: /path/to/jsmin
+
 =head1 DESCRIPTION
 
-This plugin provides auto-compilation and on-wire compression of your application's CSS and Javascript. It is enabled by default.
+This plugin provides auto-compilation and on-wire compression of your
+application's CSS and Javascript. It is enabled by default, unless
+your C<ConfigFileVersion> is greater or equal than 2.
+
+It also supports js minifier, you will need to specify the full path.
+The jsmin can be obtained from
+L<http://www.crockford.com/javascript/jsmin.html>.
+
+Note that you will need to use C<ConfigFileVersion> 2 to be able to
+configure jsmin feature.
+
+=cut
+
+__PACKAGE__->mk_accessors(qw(css js jsmin cached_javascript cached_javascript_digest cached_javascript_time ));
+
+=head2 init
+
+Initializes the compression object. Takes a paramhash containing keys
+'css' and 'js' which can be used to disable compression on files of
+that type.
+
+=cut
+
+sub init {
+    my $self = shift;
+    my %opt  = @_;
+    $self->css( $opt{css} );
+    $self->js( $opt{js} );
+    $self->jsmin( $opt{jsmin} );
+
+    Jifty::Web->add_trigger(
+        name      => 'include_javascript',
+        callback  => sub { $self->_include_javascript(@_) },
+        abortable => 1,
+    ) if $self->js_enabled;
+}
+
+=head2 js_enabled
+
+Returns whether JS compression is enabled (which it is by default)
 
 =cut
 
+sub js_enabled {
+    my $self = shift;
+    defined $self->js ? $self->js : 1;
+}
+
+=head2 css_enabled
+
+Returns whether CSS compression is enabled (which it is by default)
+
+=cut
+
+sub css_enabled {
+    my $self = shift;
+    defined $self->css ? $self->css : 1;
+}
+
+sub _include_javascript {
+    my $self = shift;
+
+    $self->_generate_javascript;
+    Jifty->web->out( qq[<script type="text/javascript" src="/__jifty/js/]
+            . $self->cached_javascript_digest
+            . qq[.js"></script>] );
+    return 0;
+}
+
+=head3 _generate_javascript
+
+Checks if the compressed JS is generated, and if it isn't, generates
+and caches it.
+
+=cut
+
+sub _generate_javascript {
+    my $self = shift;
+
+    if ( not defined $self->cached_javascript_digest
+        or Jifty->config->framework('DevelMode') ) {
+        Jifty->log->debug("Generating JS...");
+
+        my @roots = (
+            Jifty::Util->absolute_path(
+                File::Spec->catdir(
+                    Jifty->config->framework('Web')->{'StaticRoot'}, 'js'
+                )
+            ),
+
+            Jifty::Util->absolute_path(
+                File::Spec->catdir(
+                    Jifty->config->framework('Web')->{'DefaultStaticRoot'},
+                    'js'
+                )
+            ),
+        );
+
+        my $js = "";
+
+        for my $file ( @{ Jifty::Web->javascript_libs } ) {
+            my $include;
+
+            for my $root (@roots) {
+                my @spec = File::Spec->splitpath( $root, 1 );
+                my $path = File::Spec->catpath( @spec[ 0, 1 ], $file );
+
+                if ( -e $path ) {
+                    $include = $path;
+                    last;
+                }
+            }
+
+            if ( defined $include ) {
+                my $fh;
+
+                if ( open $fh, '<', $include ) {
+                    $js .= "/* Including '$file' */\n\n";
+                    $js .= $_ while <$fh>;
+                    $js .= "\n/* End of '$file' */\n\n";
+                } else {
+                    $js .= "\n/* Unable to open '$file': $! */\n";
+                }
+            } else {
+                $js .= "\n/* Unable to find '$file' */\n";
+            }
+        }
+        if ($self->jsmin) {
+            eval { $self->minify_js(\$js) };
+            Jifty->log->error("Unable to run jsmin: $@") if $@;
+        }
+        $self->cached_javascript($js);
+        $self->cached_javascript_digest( md5_hex($js) );
+        $self->cached_javascript_time(time);
+    }
+}
+
+sub minify_js {
+    my $self = shift;
+    my $input = shift;
+    my $output;
+
+    Jifty->log->debug("Minifying JS...");
+
+    # We need to reopen stdout temporarily, because in FCGI
+    # environment, stdout is tied to FCGI::Stream, and the child
+    # of the run3 wouldn't be able to reopen STDOUT properly.
+    my $stdout = IO::Handle->new;
+    $stdout->fdopen( 1, 'w' );
+    local *STDOUT = $stdout;
+
+    my $stderr = IO::Handle->new;
+    $stderr->fdopen( 2, 'w' );
+    local *STDERR = $stderr;
+
+    local $SIG{'CHLD'} = 'DEFAULT';
+    run3 [$self->jsmin], $input, \$output, undef;
+
+    $$input = $output;
+}
 
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm	Tue Jun 12 15:48:07 2007
@@ -13,13 +13,13 @@
 which serve out compiled and compressed CSS and Javascript rules.
 
 =cut
-
+use Compress::Zlib qw();
+use HTTP::Date ();
 
 use Jifty::Dispatcher -base;
 
 on '/__jifty/js/*' => run {
     my $arg = $1;
-    warn "My arg is $arg";
     if ( $arg !~ /^[0-9a-f]{32}\.js$/ ) {
 
         # This doesn't look like a real request for squished JS,
@@ -27,12 +27,13 @@
         Jifty->web->redirect( "/static/js/" . $arg );
     }
 
-    Jifty->web->generate_javascript;
+    my ($ccjs) = Jifty->find_plugin('Jifty::Plugin::CompressedCSSandJS')
+        or Jifty->web->redirect( "/static/js/" . $arg );
 
-    use HTTP::Date ();
+    $ccjs->_generate_javascript;
 
     if ( Jifty->handler->cgi->http('If-Modified-Since')
-        and $arg eq Jifty->web->cached_javascript_digest . '.js' )
+        and $arg eq $ccjs->cached_javascript_digest . '.js' )
     {
         Jifty->log->debug("Returning 304 for cached javascript");
         Jifty->handler->apache->header_out( Status => 304 );
@@ -46,18 +47,16 @@
     # XXX TODO: If we start caching the squished JS in a file somewhere, we
     # can have the static handler serve it, which would take care of gzipping
     # for us.
-    use Compress::Zlib qw();
-
     if ( Jifty::View::Static::Handler->client_accepts_gzipped_content ) {
         Jifty->log->debug("Sending gzipped squished JS");
         Jifty->handler->apache->header_out( "Content-Encoding" => "gzip" );
         Jifty->handler->apache->send_http_header();
         binmode STDOUT;
-        print Compress::Zlib::memGzip( Jifty->web->cached_javascript );
+        print Compress::Zlib::memGzip( $ccjs->cached_javascript );
     } else {
         Jifty->log->debug("Sending squished JS");
         Jifty->handler->apache->send_http_header();
-        print Jifty->web->cached_javascript;
+        print $ccjs->cached_javascript;
     }
     abort;
 };
@@ -74,8 +73,6 @@
 
     Jifty->web->generate_css;
 
-    use HTTP::Date ();
-
     if ( Jifty->handler->cgi->http('If-Modified-Since')
         and $arg eq Jifty->web->cached_css_digest . '.css' )
     {
@@ -90,8 +87,6 @@
     # XXX TODO: If we start caching the squished CSS in a file somewhere, we
     # can have the static handler serve it, which would take care of gzipping
     # for us.
-    use Compress::Zlib qw();
-
     if ( Jifty::View::Static::Handler->client_accepts_gzipped_content ) {
         Jifty->log->debug("Sending gzipped squished CSS");
         Jifty->handler->apache->header_out( "Content-Encoding" => "gzip" );
@@ -105,4 +100,5 @@
     }
     abort;
 };
+
 1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Feedback;
+use base qw/Jifty::Plugin Class::Accessor/;
+
+# Your plugin goes here.  If takes any configuration or arguments, you
+# probably want to override L<Jifty::Plugin/init>.
+
+=head1 NAME
+
+Jifty::Plugin::Feedback
+
+=head1 DESCRIPTION
+
+This plugin provides a "feedback box" for your app.
+
+
+Add to your app's config:
+
+  Plugins: 
+    - Feedback: 
+        from: defaultsender at example.com
+        to: recipient at example.com
+
+Add to your app's UI where you want the feedback box:
+
+ show '/feedback/request_feedback';
+
+
+=cut
+
+__PACKAGE__->mk_accessors(qw(from to));
+
+=head2 init
+
+Initializes the Feedback object. Takes a paramhash with keys 'from' and 'to', which are email addresses.
+
+=cut
+
+sub init {
+    my $self = shift;
+    my %opt = @_;
+    $self->from($opt{'from'});
+    $self->to($opt{'to'});
+}
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,95 @@
+
+use warnings;
+use strict;
+
+=head1 NAME
+
+Jifty::Plugin::Feedback::Action::SendFeedback
+
+=cut
+
+package Jifty::Plugin::Feedback::Action::SendFeedback;
+use base qw/Jifty::Action/;
+
+
+=head2 arguments
+
+The fields for C<SendFeedback> are:
+
+=over 4
+
+=item content: a big box where the user can type in what eits them
+
+
+=back
+
+=cut
+
+sub arguments {
+        {
+            content => {
+                    label   => '',
+                    render_as => 'Textarea',
+                    rows => 5,
+                    cols => 60,
+                    sticky => 0
+            },
+        }
+
+}
+
+=head2 take_action
+
+Send some mail to the hiveminders describing the issue.
+
+=cut
+
+sub take_action {
+    my $self = shift;
+    return 1 unless ( $self->argument_value('content') );
+
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Feedback');
+    my $debug_info = $self->build_debugging_info();
+
+    my $msg = $self->argument_value('content') . "\n\n" . $debug_info;
+    my $subject = substr( $self->argument_value('content'), 0, 60 );
+    $subject =~ s/\n/ /g;
+
+    # Fall back to normal email
+    my $mail = Jifty::Notification->new;
+    $mail->body($msg);
+    $mail->from(
+        (          Jifty->web->current_user->id
+                && Jifty->web->current_user->user_object->can('email')
+        )
+        ? Jifty->web->current_user->user_object->email()
+        : $plugin->from
+    );
+    $mail->recipients( $plugin->to );
+    $mail->subject( "["
+            . Jifty->config->framework('ApplicationName')
+            . " feedback] "
+            . $subject );
+    $mail->send_one_message;
+
+    $self->result->message(qq[Thanks for the feedback. We appreciate it!]);
+    return 1;
+}
+
+=head2 build_debugging_info
+
+Strings together the current environment to attach to outgoing
+email. Returns it as a scalar.
+
+=cut
+
+sub build_debugging_info {
+    my $self = shift;
+    my $message = "-- \nPrivate debugging information:\n";
+    $message   .= " $_: $ENV{$_}\n"
+      for sort grep {/^(HTTP|REMOTE|REQUEST)_/} keys %ENV;
+
+    return $message;
+}
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/View.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,53 @@
+use warnings;
+use strict;
+package Jifty::Plugin::Feedback::View;
+
+use Jifty::View::Declare -base;
+
+
+template 'feedback/request_feedback' => sub {
+    div {
+        attr { id => 'feedback_wrapper' };
+
+        h3 { _('Send us feedback!') } p {
+            "Tell us what's good, what's bad, and what else you want "
+                . Jifty->config->framework('ApplicationName')
+                . " to do!";
+        };
+        render_region(
+            'feedback',
+            path     => "/feedback/region",
+            defaults => {}
+        );
+    };
+};
+
+
+template 'feedback/region' => sub {
+    my $feedback = Jifty->web->new_action(
+        class   => "SendFeedback",
+        moniker => "feedback"
+    );
+
+    if ( Jifty->web->response->result("feedback")) { 
+    span {
+        attr { id => 'feedback-result' };
+        Jifty->web->response->result("feedback")->{'message'};
+    };
+    };
+    div {
+        attr { id => 'feedback' };
+
+        form {
+            render_param( $feedback => 'content' );
+            form_submit(
+                label   => "Send",
+                onclick => {
+                    submit       => $feedback,
+                    refresh_self => 1
+                }
+            );
+            }
+        }
+};
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,7 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::OpenID;
+use base qw/Jifty::Plugin/;
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/AuthenticateOpenID.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/AuthenticateOpenID.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,81 @@
+use warnings;
+use strict;
+
+=head1 NAME
+
+Jifty::Plugin::OpenID::Action::AuthenticateOpenID;
+
+=cut
+
+package Jifty::Plugin::OpenID::Action::AuthenticateOpenID;
+
+use base qw/Jifty::Action/;
+
+use LWPx::ParanoidAgent;
+use Net::OpenID::Consumer;
+use Cache::FileCache;
+
+=head2 arguments
+
+Return the OpenID URL field
+
+=cut
+
+use Jifty::Param::Schema;
+use Jifty::Action schema {
+    param 'openid' =>
+        label is _('OpenID URL'),
+        is mandatory,
+        hints is 'For example: you.livejournal.com';
+
+    param 'return_to' =>
+        render as 'Hidden',
+        default is '/openid/verify_and_login';
+};
+
+=head2 take_action
+
+Creates local user if non-existant and redirects to OpenID auth URL
+
+=cut
+
+sub take_action {
+    my $self   = shift;
+    my $openid = $self->argument_value('openid');
+    my $path   = $self->argument_value('return_to');
+
+    my $baseurl = Jifty->web->url;
+    my $csr = Net::OpenID::Consumer->new(
+        ua    => LWPx::ParanoidAgent->new,
+        cache => Cache::FileCache->new,
+        args  => scalar Jifty->handler->cgi->Vars,
+        consumer_secret => Jifty->config->app('OpenIDSecret'),
+        required_root => $baseurl
+    );
+
+    my $claimed_id = $csr->claimed_identity( $openid );
+
+    if ( not defined $claimed_id ) {
+        $self->result->error(_("Invalid OpenID URL.  Please check to make sure it is correct.  (@{[$csr->err]})"));
+        return;
+    }
+
+    $openid = $claimed_id->claimed_url;
+
+    my $return_to = Jifty->web->url( path => $path );
+    if(Jifty->web->request->continuation) {
+        $return_to .= ($return_to =~ /\?/) ? '&' : '?';
+        $return_to .= "J:C=" . Jifty->web->request->continuation->id;
+    }
+
+    my $check_url = $claimed_id->check_url( 
+                        return_to  => $return_to,
+                        trust_root => $baseurl,
+                        delayed_return => 1
+                    );
+
+    Jifty->web->_redirect( $check_url . '&openid.sreg.optional=nickname' );
+    return 1; # should never get here
+}
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,102 @@
+use strict;
+use warnings;
+
+=head1 NAME
+
+Jifty::Plugin::OpenID::Action::CreateOpenIDUser
+
+=cut
+
+package Jifty::Plugin::OpenID::Action::CreateOpenIDUser;
+use base qw/Jifty::Action::Record/;
+
+sub record_class {
+    Jifty->app_class("Model", "User")
+}
+
+
+=head2 arguments
+
+The fields for C<CreateOpenIDUser> are:
+
+=over 4
+
+=item name: a nickname
+
+=back
+
+=cut
+
+sub arguments {
+    my $self = shift;
+    my $args = $self->SUPER::arguments;
+
+    my %fields = (
+        name => 1,
+    );
+
+    for ( keys %$args ){
+        delete $args->{$_} unless $fields{$_};
+    }
+
+    $args->{'name'}{'ajax_validates'} = 1;
+    return $args;
+}
+
+
+=head2 take_action
+
+=cut
+
+sub take_action {
+    my $self = shift;
+
+    my $openid = Jifty->web->session->get('openid');
+
+    if ( not $openid ) {
+        # Should never get here unless someone's trying weird things
+        $self->result->error("Invalid verification result: '$openid'");
+        return;
+    }
+
+    my $user = Jifty->app_class("Model", "User")->new(current_user => Jifty->app_class("CurrentUser")->superuser );
+
+    $user->load_by_cols( openid => $openid );
+
+    if ( $user->id ) {
+        $self->result->error( "That OpenID already has an account.  Something's gone wrong." );
+        return;
+    }
+
+    $user->create( openid => $openid, name => $self->argument_value('name') );
+
+    if ( not $user->id ) {
+        $self->result->error( "Something bad happened and we couldn't log you in.  Please try again later." );
+        return;
+    }
+
+    my $current_user = Jifty->app_class("CurrentUser")->new( openid => $openid );
+
+    # Actually do the signin thing.
+    Jifty->web->current_user($current_user);
+    Jifty->web->session->expires( undef );
+    Jifty->web->session->set_cookie;
+
+    $self->report_success if not $self->result->failure;
+    Jifty->web->session->remove('openid');
+
+    return 1;
+}
+
+=head2 report_success
+
+=cut
+
+sub report_success {
+    my $self = shift;
+    # Your success message here
+    $self->result->message( _("Welcome, ") . Jifty->web->current_user->username . "." );
+}
+
+1;
+

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/VerifyOpenID.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Action/VerifyOpenID.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,73 @@
+use warnings;
+use strict;
+
+=head1 NAME
+
+Jifty::Plugin::OpenID::Action::VerifyOpenID;
+
+=cut
+
+package Jifty::Plugin::OpenID::Action::VerifyOpenID;
+use base qw/Jifty::Action/;
+
+use Net::OpenID::Consumer;
+use Cache::FileCache;
+use LWPx::ParanoidAgent;
+
+=head2 arguments
+
+No args
+
+=cut
+
+sub arguments { return ( {} ) }
+
+=head2 take_action
+
+Check the result of the login.  If it's good, load the user
+and log them in.  Otherwise, throw an error.
+
+=cut
+
+sub take_action {
+    my $self = shift;
+
+# XXX HACK: some OpenID servers (LJ and myopenid.com included) don't seem
+# to properly escape plus signs (+) in openid.sig when returning the user
+# back to us.  We need to convert the pluses back from spaces to pluses again.
+    my $sig = Jifty->handler->cgi->param('openid.sig');
+    $sig =~ s/ /+/g;
+    Jifty->handler->cgi->param( 'openid.sig' => $sig );
+
+    my $csr = Net::OpenID::Consumer->new(
+        ua              => LWPx::ParanoidAgent->new,
+        cache           => Cache::FileCache->new,
+        args            => scalar Jifty->handler->cgi->Vars,
+        consumer_secret => Jifty->config->app('OpenIDSecret')
+    );
+
+    if ( my $setup = $csr->user_setup_url ) {
+        Jifty->web->_redirect($setup);
+    }
+    elsif ( $csr->user_cancel ) {
+        $self->result->error(
+            _(  'OpenID verification failed.  It looks like you cancelled the OpenID verification request.'
+            )
+        );
+        return;
+    }
+
+    my $ident = $csr->verified_identity;
+
+    if ( not defined $ident ) {
+        $self->result->error( _('OpenID verification failed: ') . $csr->err );
+        return;
+    }
+
+    Jifty->log( "identified as: " . $ident->url );
+    $self->result->content( openid => $ident->url );
+
+    return 1;
+}
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Dispatcher.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,95 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::OpenID::Dispatcher;
+use Jifty::Dispatcher -base;
+
+before qr'^/(?:openid/link)' => run {
+    tangent('/openid/login') unless (Jifty->web->current_user->id)
+};
+
+before qr'^/openid/login' => run {
+    set action => Jifty->web->new_action(
+        class   => 'AuthenticateOpenID',
+        moniker => 'authenticateopenid'
+    );
+};
+
+before qr'^/openid/verify' => run {
+    Jifty->web->request->add_action(
+        class   => 'VerifyOpenID',
+        moniker => 'verifyopenid'
+    );
+};
+
+on 'openid/verify_and_link' => run {
+    my $result = Jifty->web->response->result('verifyopenid');
+    my $user   = Jifty->web->current_user;
+    if ( defined $result and $result->success and $user->id ) {
+        my $openid = $result->content('openid');
+        my ( $ret, $msg ) = $user->user_object->validate_openid( $openid );
+
+        if ( not $ret ) {
+            $result->error(_("It looks like someone is already using that OpenID."));
+            redirect '/openid/link';
+        }
+        else {
+            $user->user_object->link_to_openid( $openid );
+            $result->message(_("The OpenID '$openid' has been linked to your account."));
+        }
+    }
+    redirect '/';
+};
+
+on 'openid/verify_and_login' => run {
+    my $result = Jifty->web->response->result('verifyopenid');
+
+    if ( defined $result and $result->success ) {
+        my $openid = $result->content('openid');
+        my $user = Jifty->app_class('CurrentUser')->new( openid => $openid );
+        Jifty->log->info("User Class: $user. OpenID: $openid");
+
+        if ( $user->id ) {
+            # Set up our login message
+            $result->message( _("Welcome back, ") . $user->username . "." );
+
+            # Actually do the signin thing.
+            Jifty->web->current_user($user);
+            Jifty->web->session->expires( undef );
+            Jifty->web->session->set_cookie;
+
+            if(Jifty->web->request->continuation) {
+                Jifty->web->request->continuation->call;
+            } else {
+                redirect '/';
+            }
+        }
+        else {
+            # User needs to create account still
+            Jifty->web->session->set( openid => $openid );
+            Jifty->log->info("got openid: $openid");
+            my $nick = get('openid.sreg.nickname');
+            if ( $nick ) {
+                redirect( Jifty::Web::Form::Clickable->new( url => '/openid/create', parameters => { nickname => $nick, openid => $openid } ));
+            }
+            else {
+                redirect( Jifty::Web::Form::Clickable->new( url => '/openid/create' ) );
+            }
+        }
+    }
+    else {
+        redirect '/openid/login';
+    }
+};
+
+on 'openid/create' => run {
+    if ( not Jifty->web->session->get('openid') ) {
+        redirect '/';
+    }
+
+    set action => Jifty->web->new_action( class => 'CreateOpenIDUser', parameters => { openid => Jifty->web->session->get("openid") } );
+    set 'next' => Jifty->web->request->continuation ||
+                  Jifty::Continuation->new( request => Jifty::Request->new( path => "/" ) );
+};
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,69 @@
+package Jifty::Plugin::OpenID::Mixin::Model::User;
+use strict;
+use warnings;
+use Jifty::DBI::Schema;
+use base 'Jifty::DBI::Record::Plugin';
+use URI;
+
+use Jifty::Plugin::OpenID::Record schema {
+
+our @EXPORT = qw(has_alternative_auth link_to_openid);
+
+column openid =>
+  type is 'text',
+  label is 'OpenID',
+  hints is q{You can use your OpenID to log in quickly and easily.},
+  is distinct,
+  is immutable;
+
+};
+
+sub has_alternative_auth { 1 }
+
+sub register_triggers {
+    my $self = shift;
+    $self->add_trigger(name => 'validate_openid', callback => \&validate_openid, abortable => 1);
+    $self->add_trigger(name => 'canonicalize_openid', callback => \&canonicalize_openid);
+}
+
+sub validate_openid {
+    my $self   = shift;
+    my $openid = shift;
+
+    my $uri = URI->new( $openid );
+
+    return ( 0, q{That doesn't look like an OpenID URL.} )
+        if not defined $uri;
+
+    my $temp_user = Jifty->app_class("Model", "User")->new;
+    $temp_user->load_by_cols( openid => $uri->canonical );
+
+    # It's ok if *we* have the openid we're looking for
+    return ( 0, q{It looks like somebody else has claimed that OpenID.} )
+        if $temp_user->id and ( not $self->id or $temp_user->id != $self->id );
+
+    return 1;
+}
+
+sub canonicalize_openid {
+    my $self   = shift;
+    my $openid = shift;
+
+    return ''
+        if not defined $openid or not length $openid;
+
+    $openid = 'http://' . $openid
+        if $openid !~ m{^http://};
+
+    my $uri = URI->new( $openid );
+
+    return $uri->canonical;
+}
+
+sub link_to_openid {
+    my $self   = shift;
+    my $openid = shift;
+    $self->__set( column => 'openid', value => $openid );
+}
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OpenID/View.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,60 @@
+package Jifty::Plugin::OpenID::View;
+use strict;
+use warnings;
+use Jifty::View::Declare -base;
+
+template 'openid/login' => page {
+    { title is _ "Login with your OpenID" }
+    my $action = get('action');
+
+    div {
+        unless ( Jifty->web->current_user->id ) {
+            div {
+                attr { id => 'openid-login' };
+                outs(
+                    p {
+                        em {
+                            _(  qq{If you have a Livejournal or other OpenID account, you don\'t even need to sign up. Just log in.}
+                            );
+                        }
+                    }
+                );
+                form {
+                    render_action($action);
+                    form_submit(
+                        label  => _("Go for it!"),
+                        submit => $action
+                    );
+                }
+            };
+        }
+        else {
+            outs( _("You already logged in.") );
+        }
+    }
+};
+
+template 'openid/create' => page {
+    title is 'Set your username';
+    my ( $action, $next ) = get( 'action', 'next' );
+
+    p {
+        outs(
+            _(  'We need you to set a username or quickly check the one associated with your OpenID. Your username is what other people will see when you ask questions or make suggestions'
+            )
+        );
+    };
+    p {
+        outs(
+            _(  'If the username provided conflicts with an existing username or contains invalid characters, you will have to give us a new one.'
+            )
+        );
+    };
+    Jifty->web->form->start( call => $next );
+    render_param( $action, 'name', default_value => get('nickname') );
+    form_submit( label => _('Continue'), submit => $action );
+    Jifty->web->form->end;
+};
+
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::SinglePage;
+use base qw/Jifty::Plugin Class::Accessor/;
+
+__PACKAGE__->mk_accessors(qw(region_name));
+
+sub init {
+    my $self = shift;
+    Jifty::Web::Form::Clickable->add_trigger( before_new => _sp_link($self));
+    my %opt = @_;
+    $self->region_name($opt{region_name} || '__page');
+}
+
+sub _push_onclick {
+    my $self = shift;
+    my $args = shift;
+    $args->{onclick} = [ $args->{onclick} ? $args->{onclick} : () ]
+        unless ref $args->{onclick} eq 'ARRAY';
+    push @{$args->{onclick}}, @_ if @_;
+}
+
+sub _sp_link {
+    my $self = shift;
+    return sub {
+        my ( $clickable, $args ) = @_;
+        my $url = $args->{'url'};
+        if ( $url && $url !~ m/^#/ && $url !~ m{^https?://} ) {
+            $self->_push_onclick($args, {
+                region       => $self->region_name,
+                replace_with => $url,
+                args         => $args->{parameters}});
+        }
+        elsif (exists $args->{submit}) {
+	    $self->_push_onclick($args, { refresh_self => 1, submit => $args->{submit} });
+	    $args->{as_button} = 1;
+	}
+        if (my $form = delete $args->{_form}) {
+	    $args->{call} = $form->call;
+	}
+        my $onclick = $args->{onclick};
+        if ( $args->{onclick} ) {
+            $self->_push_onclick($args);    # make sure it's array
+            for my $onclick ( @{ $args->{onclick} } ) {
+                next unless UNIVERSAL::isa($onclick, 'HASH');
+                if ( $onclick->{region} && !ref( $onclick->{region} ) ) {
+                    my $region = $self->region_name;
+                    $onclick->{region} = $region . '-' . $onclick->{region}
+                        unless $onclick->{region} eq $region
+                        or $onclick->{region} =~ m/^\Q$region\E-/;
+                }
+            }
+        }
+    }
+}
+
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage/Dispatcher.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,30 @@
+package Jifty::Plugin::SinglePage::Dispatcher;
+use strict;
+use warnings;
+use Jifty::Dispatcher -base;
+
+before '__jifty/webservices/*' => run {
+    my (@actions) = grep { $_->class eq 'Jifty::Action::Redirect' } values %{ Jifty->web->request->{'actions'} };
+    $_->active(0) for @actions;
+
+    # XXX: shouldn't have multiple redirect
+    # Simply ignore Redirect from webservice if we are not in SPA
+    set '_webservice_redirect' => [map { $_->arguments->{url} } @actions]
+        if Jifty->find_plugin('Jifty::Plugin::SinglePage');
+
+};
+
+on qr{(__jifty/webservices/.*)} => run {
+    my $actions = get '_webservice_redirect';
+    for my $act (@$actions) {
+        if ($act =~ m{^https?://}) {
+            set '_webservice_external_redirect' => $act;
+        }
+        else {
+            Jifty->web->webservices_redirect($act);
+        }
+    }
+    show $1;
+};
+
+1;

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,7 @@
+use warnings;
+use strict;
+package Jifty::Plugin::SiteNews;
+use base qw'Jifty::Plugin';
+
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm	Tue Jun 12 15:48:07 2007
@@ -7,7 +7,7 @@
 
 our @EXPORT = qw(current_user_can);
 
-use Jifty::Plugin::SiteNews::Record schema {
+use Jifty::Record schema {
 
     my $user_class = Jifty->app_class('Model', 'User');
 
@@ -25,6 +25,11 @@
   render_as is 'Textarea';
 };
 
+=head2 create
+
+Create the News model. Takes a paramhash with keys author_id, created, title, and content.
+
+=cut
 
 sub create {
     my $self = shift;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/View/News.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/View/News.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SiteNews/View/News.pm	Tue Jun 12 15:48:07 2007
@@ -11,10 +11,15 @@
 
 sub fragment_base_path {'/news'}
 
+
+template search_region => sub {''};
+
 template 'index.html' => page {
     title is  'Site news' ;
     form {
-        show('/news/list');
+            render_region(
+                name     => 'newslist',
+                path     => '/news/list');
     }
 
 };

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SkeletonApp/View.pm	Tue Jun 12 15:48:07 2007
@@ -26,9 +26,7 @@
         if (    Jifty->web->current_user->id
             and Jifty->web->current_user->user_object )
         {
-            my $u      = Jifty->web->current_user->user_object;
-            my $method = $u->_brief_description;
-            eval {_( 'Hiya, %1.', $u->$method() )};
+            _( 'Hiya, %1.', Jifty->web->current_user->username );
         }
         else {
             _("You're not currently signed in.");
@@ -69,7 +67,8 @@
 };
 
 private template 'keybindings' => sub {
-    div { id is "keybindings" };
+    div { id is "keybindings";
+      outs_raw('<script type="text/javascript"><!-- Jifty.KeyBindings.reset() --></script>') };
 };
 
 #template 'index.html' => page { { title is _('Welcome to your new Jifty application') } img { src is "/static/images/pony.jpg", alt is _( 'You said you wanted a pony. (Source %1)', 'http://hdl.loc.gov/loc.pnp/cph.3c13461'); }; };

Added: jifty/branches/virtual-models/lib/Jifty/Plugin/TabView/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/TabView/View.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,77 @@
+package Jifty::Plugin::TabView::View;
+use strict;
+use warnings;
+
+# XXX: To be converted to a plugin with included css and images.
+
+use Jifty::View::Declare -base;
+
+use base 'Exporter';
+our @EXPORT = qw(render_tabs);
+
+=head1 NAME
+
+Jifty::Plugin::TabView::View - render tabview using yui tabs
+
+=head1 SYNOPSIS
+
+  use Jifty::Plugin::TabView::View;
+  template 'index.html' => page {
+     my $self = shift;
+     $self->render_tabs('foo', [qw(id)], qw( foo bar_tab ) );
+  };
+  template 'foo' => sub { ... };
+  template 'bar' => sub { ... };
+
+=head2 render_tabs
+
+Returns some Template::Declare with tabs rendered with the yui tabs
+If a tab ends in _tab, it means it should contain a stub region to be
+replaced by the corresponding fragment onclick to that tab.
+
+=cut
+
+sub render_tabs {
+    my ($self, $divname, $args, @tabs) = @_;
+
+    outs_raw(qq'<script type="text/javascript">
+	var myTabs = new YAHOO.widget.TabView("$divname");
+	</script>'  );
+
+
+    div { { id is $divname, class is 'yui-navset'}
+	  ul { { class is 'yui-nav'};
+	       my $i = 0;
+	       for (@tabs) {
+		   my $tab = $_;
+		   li { { class is 'selected' unless $i };
+			hyperlink(url => '#tab'.++$i, label => $tab,
+				  $tab =~ s/_tab$// ? 
+				  (onclick =>
+				  { region       => Jifty->web->current_region->qualified_name."-$tab-tab",
+				    replace_with => $tab,#$self->fragment_for($tab),
+				    args => { map { $_ => get($_)} @$args },
+				  }) : ()
+				 ) }
+	       }
+	   };
+	  div { {class is 'yui-content' };
+      my $default_shown;
+		for (@tabs) {
+		    div { 
+			if (s/_tab$//) {
+			    render_region(name => $_.'-tab', 
+                          ($default_shown++)? () : ( path => $_, args =>  { map { $_ => get($_)} @$args })
+                          )
+			}
+			else {
+			    die "$self $_" unless $self->has_template($_);
+			    $self->has_template($_)->(); 
+			}
+		    }
+		}
+	    }
+      };
+};
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/Record.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Record.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Record.pm	Tue Jun 12 15:48:07 2007
@@ -215,19 +215,93 @@
 Called before any attribute is changed on the object.
 ATTRIBUTES is a hash of the arguments passed to _set.
 
-
-
 =item delete
 
 Called before the object is deleted.
 
 =back
 
-The default implementation returns true if the current user is a
-superuser or a boostrap user.  If the user is looking to delegate the
-access control decision to another object (by creating a
-C<delegate_current_user_can> subroutine), it hands off to that
-routine.  Otherwise, it returns false.
+Models wishing to customize authorization checks should override this method. You can do so like this:
+
+  sub current_user_can {
+      my ($self, $right, %args) = @_;
+
+      # Make any custom checks that return 1 to allow or return 0 to deny...
+      
+      # Fallback upon the default implementation to handle the
+      # SkipAccessControl configuration setting, superuser, bootstrap,
+      # delegation, and the before_access hook
+      return $self->SUPER::current_user_can($right, %args);
+  }
+
+If you are sure you don't want your model to fallback using the default implementation, you can replace the last line with whatever fallback policy required.
+
+=head3 Authorization steps
+
+The default implementation proceeds as follows:
+
+=over
+
+=item 1.
+
+If the C<SkipAccessControl> setting is set to a true value in the framework configuration section of F<etc/config.yml>, C<current_user_can> always returns true.
+
+=item 2.
+
+The method first attempts to call the C<before_access> hooks to check for any
+allow or denial. See L</The before_access hook>.
+
+=item 3.
+
+Next, the default implementation returns true if the current user is a
+superuser or a boostrap user.  
+
+=item 4.
+
+Then, if the model can perform delegation, usually by using
+L<Jifty::RightsFrom>, the access control decision is deferred to another object
+(via the C<delegate_current_user_can>
+subroutine).  
+
+=item 5.
+
+Otherwise, it returns false.
+
+=back
+
+=head3 The before_access hook
+
+This implementation may make use of a trigger called C<before_access> to make the decision. A new handler can be added to the trigger point by calling C<add_handler>:
+
+  $record->add_trigger(
+      name => 'before_access',
+      code => \&before_access,
+      abortable => 1,
+  );
+
+The C<before_access> handler will be passed the same arguments that were used to call C<current_user_can>, including the current record object, the operation being checked, and any arguments being passed to the operation.
+
+The C<before_access> handler should return one of three strings: C<'deny'>, C<'allow'>, or C<'ignore'>. The C<current_user_can> implementation reacts as follows to these results:
+
+=over
+
+=item 1.
+
+If a handler is abortable and aborts by returning a false value (such as C<undef>), C<current_user_can> returns false.
+
+=item 2.
+
+If any handler returns 'deny', C<current_user_can> returns false.
+
+=item 3.
+
+If any handler returns 'allow' and no handler returns 'deny', C<current_user_can> returns true.
+
+=item 4.
+
+In all other cases, the results of the handlers are ignored and C<current_user_can> proceeds to check using superuser, bootstrap, and delegation.
+
+=back
 
 =cut
 
@@ -235,10 +309,37 @@
     my $self  = shift;
     my $right = shift;
     
+    # Turn off access control for the whole application
     if (Jifty->config->framework('SkipAccessControl')) {
-	return 1;	
+	    return 1;	
     }
 
+    my $hook_status = $self->call_trigger( before_access => $right, @_ );
+
+    # If not aborted...
+    if (defined $hook_status) {
+
+        # Compile the handler results
+        my %results;
+        $results{ $_->[0] }++ for (@{ $self->last_trigger_results });
+
+        # Deny always takes precedent
+        if ($results{deny}) {
+            return 0;
+        }
+
+        # Then allow...
+        elsif ($results{allow}) {
+            return 1;
+        }
+       
+        # Otherwise, no instruction from the handlers, move along...
+    }
+
+    # Abort! Return false for safety
+    else {
+        return 0;
+    } 
 
     if (   $self->current_user->is_bootstrap_user
         or $self->current_user->is_superuser )
@@ -390,6 +491,18 @@
     $self->SUPER::delete(@_); 
 }
 
+=head2 brief_description
+
+Display the friendly name of the record according to _brief_description.
+
+=cut
+
+sub brief_description {
+    my $self = shift;
+    my $method = $self->_brief_description;
+    return $self->$method;
+}
+
 =head2 _brief_description
 
 When displaying a list of records, Jifty can display a friendly value 
@@ -490,22 +603,20 @@
 
 }
 
-=head2 drop_table_in_db
+=head2 drop_table_in_db 
 
-When called, this method will drop the table that backs this model class from your 
-database. Generally, this operation can't be done and will result in data loss.
-Don't call it unless data loss is your objective.
+When called, this method will generate the SQL to remove this model's
+table in the database and execute it in the application's currently
+open database.  This method can destroy a lot of data. Be sure you
+know what you're doing.
 
 
 =cut
 
 sub drop_table_in_db {
-    my $class = shift;
-
-    my $statement = "DROP TABLE ".$class->table;
-        my $ret = Jifty->handle->simple_query($statement);
-        $ret or die "error creating table $class: " . $ret->error_message;
-
+    my $self = shift;
+    my $ret  = Jifty->handle->simple_query( 'DROP TABLE ' . $self->table );
+    $ret or die "error removing table $self: " . $ret->error_message;
 }
 
 sub _make_schema { 
@@ -521,7 +632,7 @@
 
 =head2 add_column_sql column_name
 
-Returns the SQL statement neccessary to add C<column_name> to this class's representation in the database
+Returns the SQL statement necessary to add C<column_name> to this class's representation in the database
 
 =cut
 
@@ -534,9 +645,23 @@
     return "ALTER TABLE " . $self->table . " ADD COLUMN " . $definition;
 }
 
+
+=head2 add_column_in_db column_name
+
+Executes the SQL code generated by add_column_sql. Dies on failure.
+
+=cut
+
+sub add_column_in_db {
+    my $self = shift;
+        my $ret = Jifty->handle->simple_query($self->add_column_sql(@_));
+        $ret or die "error adding column ". $_[0] ." to  $self: " . $ret->error_message;
+
+}
+
 =head2 drop_column_sql column_name
 
-Returns the SQL statement neccessary to remove C<column_name> from this class's representation in the database
+Returns the SQL statement necessary to remove C<column_name> from this class's representation in the database
 
 =cut
 
@@ -561,6 +686,20 @@
     return $self->_handle->lookup_uuid($table, $id);
 }
 
+
+=head2 drop_column_in_db column_name
+
+Executes the SQL code generated by drop_column_sql. Dies on failure.
+
+=cut
+
+sub drop_column_in_db {
+    my $self = shift;
+        my $ret = Jifty->handle->simple_query($self->drop_column_sql(@_));
+        $ret or die "error dropping column ". $_[0] ." to  $self: " . $ret->error_message;
+
+}
+
 =head2 schema_version
 
 This method is used by L<Jifty::DBI::Record> to determine which schema version is in use. It returns the current database version stored in the configuration.

Modified: jifty/branches/virtual-models/lib/Jifty/Request.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Request.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Request.pm	Tue Jun 12 15:48:07 2007
@@ -221,7 +221,9 @@
     my $self = shift;
     my ($cgi) = @_;
 
-    my $path = $cgi->path_info;
+    # always get the unescaped path for dispatcher, which is already
+    # the case for fastcgi, but not standalone.
+    my $path = URI::Escape::uri_unescape($cgi->path_info);
     $path =~ s/\?.*//;
     $self->path( $path );
 
@@ -530,8 +532,7 @@
     return if $self->is_subrequest;
     return unless $self->continuation_type and $self->continuation_type eq "call" and $self->continuation;
     $self->log->debug("Calling continuation ".$self->continuation->id);
-    $self->continuation->call;
-    return 1;
+    return $self->continuation->call;
 }
 
 =head2 return_from_continuation

Modified: jifty/branches/virtual-models/lib/Jifty/RightsFrom.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/RightsFrom.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/RightsFrom.pm	Tue Jun 12 15:48:07 2007
@@ -23,7 +23,7 @@
 task. L<Jifty::Record/current_user_can> uses this method to make an
 access control decision if it exists.
 
-Note that this means that you a model class can use Jifty::RightsFrom,
+Note that this means that a model class can use Jifty::RightsFrom,
 and still have a custom C<current_user_can> method, and they will not
 interfere with each other.
 

Added: jifty/branches/virtual-models/lib/Jifty/Schema.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Schema.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,305 @@
+use warnings;
+use strict;
+
+package Jifty::Schema;
+use base qw/Jifty::Object/;
+use SQL::ReservedWords;
+
+Jifty::Module::Pluggable->import(
+    require     => 1,
+    search_path => ["SQL::ReservedWords"],
+    sub_name    => '_sql_dialects',
+);
+
+our %_SQL_RESERVED          = ();
+our @_SQL_RESERVED_OVERRIDE = qw(value);
+foreach my $dialect ( 'SQL::ReservedWords', &_sql_dialects ) {
+    foreach my $word ( $dialect->words ) {
+        push @{ $_SQL_RESERVED{ lc($word) } }, $dialect->reserved_by($word);
+    }
+}
+
+# XXX TODO: QUESTIONABLE ENGINEERING DECISION
+# The SQL standard forbids columns named 'value', but just about everone on the planet
+# actually supports it. Rather than going about scaremongering, we choose
+# not to warn people about columns named 'value'
+
+delete $_SQL_RESERVED{ lc($_) } for (@_SQL_RESERVED_OVERRIDE);
+
+=head2 new
+
+Returns a new Jifty::Schema. Takes no arguments. Will automatically figure out and initialize the models defined in the app's source.
+
+=cut
+
+sub new {
+    my $class = shift;
+    my $self = {};
+    bless $self, $class;
+    $self->_init_model_list();
+    return $self;
+
+}
+
+=head2 _init_model_list
+
+Reads in our application class from the config file and finds all our app's models.
+
+=cut
+
+sub _init_model_list {
+
+    my $self = shift;
+
+    # This creates a sub "models" which when called, finds packages under
+    # the application's ::Model, requires them, and returns a list of their
+    # names.
+    Jifty::Module::Pluggable->import(
+        require     => 1,
+        except      => qr/\.#/,
+        search_path => [ "Jifty::Model", Jifty->app_class("Model") ],
+        sub_name    => 'models',
+    );
+}
+
+=head2 serialize_current_schema
+
+Returns a serialization of the models in the app
+
+=cut
+
+sub serialize_current_schema {
+    my $self = shift;    
+   
+    my @models = $self->model_classes;
+    my $serialized_models = {};
+    foreach my $model (@models) {
+        $serialized_models->{$model->_class_name} = $model->serialize_metadata;
+    }
+
+    return $serialized_models;
+
+}
+
+
+=head2 upgrade_schema
+
+Looks at the current schemas as defined by the source and the database and updates the database by adding, dropping, and renaming columns.
+
+=cut
+
+sub upgrade_schema {
+    my $self = shift;
+
+
+    # load the database schema version
+
+    # hashref
+    my $old_tables = $self->current_db_schema;
+
+    # hashref
+    my $new_tables = $self->new_db_schema;
+
+    my $add_tables = {};
+    my $remove_tables ={};
+    my $add_columns = {};
+    my $remove_columns = {};
+
+    # diff the current schema version and the database schema version
+    foreach my $table ( keys %$old_tables ) {
+        unless ( $new_tables->{$table} ) {
+            $remove_tables->{$table} = $old_tables->{$table};
+            next;
+        }
+
+        foreach my $column ( @{ $old_tables->{$table}->columns } ) {
+
+     # if the column isn't in the new table as well, then mark it for deletion
+            unless ( $new_tables->{$table}->column($column) ) {
+                push @{ $remove_columns->{$table} }, $column;
+            }
+
+        # XXX TODO: compare the column definitions and alter them if necessary
+
+        }
+    }
+
+    foreach my $table ( keys %$new_tables ) {
+        unless ( $old_tables->{$table} ) {
+            $add_tables->{$table} = $new_tables->{$table};
+            next;
+        }
+
+        foreach my $column ( @{ $new_tables->{$table}->columns } ) {
+
+     # if the column isn't in the old table as well, then mark it for addition
+            unless ( $old_tables->{$table}->column($column) ) {
+                push @{ $add_columns->{$table} }, $column;
+            }
+
+        # XXX TODO: compare the column definitions and alter them if necessary
+
+        }
+    }
+
+    # Run all "Rename" rules
+    $self->run_upgrade_rules('before_all_renames');
+    my $table_renames  = Jifty->upgrade->table_renames;
+    my $column_renames = Jifty->upgrade->column_renames;
+    $self->run_upgrade_rules('after_column_renames');
+
+    $self->_add_tables($add_tables);
+    $self->_add_columns($add_columns);
+    $self->_drop_tables($remove_tables);
+    $self->_drop_columns($remove_columns);
+
+
+}
+
+
+sub _add_tables {
+    my $self = shift;
+    my $add_tables = shift;
+
+
+    # add all new tables
+    $self->run_upgrade_rules('before_table_adds');
+    foreach my $table ( values %$add_tables ) {
+        $self->run_upgrade_rules( 'before_add_table_' . $table );
+        $add_tables->{$table}->create_table_in_db();
+        $self->run_upgrade_rules( 'after_add_table_' . $table );
+    }
+    $self->run_upgrade_rules('after_table_adds');
+}
+
+
+sub _add_columns {
+    my $self = shift;
+    my $add_columns = shift;
+
+    $self->run_upgrade_rules('before_column_adds');
+    foreach my $table ( values %$add_columns ) {
+            $self->run_upgrade_rules( 'before_add_columns_to_table_' . $table );
+        my @cols = @{ $add_columns->{$table} };
+        foreach my $col (@cols) {
+            $self->run_upgrade_rules( 'before_add_column_' . $col->name . '_to_table_' . $table );
+            $add_columns->{$table}->add_column_in_db($col);
+            $self->run_upgrade_rules( 'after_add_column_' . $col->name . '_to_table_' . $table );
+        }
+            $self->run_upgrade_rules( 'after_add_columns_to_table_' . $table );
+    }
+    $self->run_upgrade_rules('after_add_columns');
+
+}
+
+
+   
+
+sub _drop_tables {
+    my $self  =shift;
+    my $remove_tables = shift;
+
+
+    $self->run_upgrade_rules('before_drop_tables');
+
+    foreach my $table ( values %$remove_tables ) {
+        $self->run_upgrade_rules( 'before_drop_table_' . $table );
+        $remove_tables->{$table}->drop_table_in_db();
+        $self->run_upgrade_rules( 'after_drop_table_' . $table );
+    }
+    $self->run_upgrade_rules('after_drop_tables');
+
+}
+
+sub _drop_columns {
+    my $self = shift;
+    my $remove_columns = shift;
+
+    $self->run_upgrade_rules('before_drop_columns');
+
+    foreach my $table ( values %$remove_columns ) {
+            $self->run_upgrade_rules( 'before_drop_columns_from_' . $table );
+        my @cols = @{ $remove_columns->{$table} };
+        foreach my $col (@cols) {
+            $self->run_upgrade_rules( 'before_drop_column' . $col->name . '_from_' . $table );
+            $remove_columns->{$table}->drop_column_in_db($col);
+            $self->run_upgrade_rules( 'after_drop_column_' . $col->name . '_from_' . $table );
+        }
+            $self->run_upgrade_rules( 'after_drop_columns_from_' . $table );
+    }
+    $self->run_upgrade_rules('after_drop_columns');
+
+}
+
+
+=head2 run_upgrade_rules rule_name
+
+This method runs all upgrade rules for the rule named C<rule_name>.
+
+=cut
+
+sub run_upgrade_rules {
+    my $self = shift;
+    my $rule_name = shift;
+
+   my $upgrade_object = Jifty->app_class('Upgrade');
+   $upgrade_object->call_trigger($rule_name);
+}
+
+
+
+sub _check_reserved {
+    my $self  = shift;
+    my $model = shift;
+    my $log   = Log::Log4perl->get_logger("SchemaTool");
+    foreach my $col ( $model->columns ) {
+        if ( exists $_SQL_RESERVED{ lc( $col->name ) } ) {
+            $log->error(
+                      $model . ": "
+                    . $col->name
+                    . " is a reserved word in these SQL dialects: "
+                    . join( ', ',
+                    _classify_reserved_words( @{ $_SQL_RESERVED{ lc( $col->name ) } } ) )
+            );
+        }
+    }
+}
+
+sub _classify_reserved_words {
+    my %dbs;
+
+    # Guess names of databases + their versions by breaking on last space,
+    # e.g., "SQL Server 7" is ("SQL Server", "7"), not ("SQL", "Server 7").
+    push @{ $dbs{ $_->[0] } }, $_->[1]
+        for map { [ split /\s+(?!.*\s)/, $_, 2 ] } @_;
+    return
+        map { join " ", $_, __parenthesize_sql_variants( @{ $dbs{$_} } ) } sort keys %dbs;
+}
+
+sub __parenthesize_sql_variants {
+    if ( not defined $_[0] ) { return () }
+    if ( @_ == 1 )           { return $_[0] }
+    return "(" . ( join ", ", @_ ) . ")";
+}
+
+=head2 connect_to_db_for_management
+
+Returns a database handle suitable for direct manipulation.
+
+=cut
+
+sub connect_to_db_for_management {
+    my $handle = Jifty::Handle->new();
+
+    my $driver   = Jifty->config->framework('Database')->{'Driver'};
+
+    # Everything but the template1 database is assumed
+    my %connect_args;
+    $connect_args{'database'} = 'template1' if ( $handle->isa("Jifty::DBI::Handle::Pg") );
+    $connect_args{'database'} = ''          if ( $handle->isa("Jifty::DBI::Handle::mysql") );
+    $handle->connect(%connect_args);
+    return $handle;
+}
+
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/Script/App.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Script/App.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Script/App.pm	Tue Jun 12 15:48:07 2007
@@ -147,7 +147,7 @@
 sub _write_config {
     my $self = shift;
     my $cfg = Jifty::Config->new(load_config => 0);
-    my $default_config = $cfg->guess($self->dist_name);
+    my $default_config = $cfg->initial_config($self->dist_name);
     my $file = join("/",$self->prefix, 'etc','config.yml');
     print("Creating configuration file $file\n");
     Jifty::YAML::DumpFile($file => $default_config);

Added: jifty/branches/virtual-models/lib/Jifty/Script/Console.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Script/Console.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,65 @@
+use strict;
+use warnings;
+
+package Jifty::Script::Console;
+use base qw/App::CLI::Command/;
+use Devel::EvalContext;
+use Term::ReadLine;
+
+=head1 NAME
+
+Jifty::Script::Console - A console for your Jifty application
+
+=head1 DESCRIPTION
+
+This script aims for developing purpose (or maintaining, if possible).
+With this script, you can say something like this to diagnose your
+application:
+
+    % bin/jifty console
+    jifty> my $foo = Jifty->app_class("Model", "StuffCollection")->new;
+    jifty> $foo->unlimit; YAML::Dump($foo)
+
+All Perl code are ok, since each lines of input are send to
+C<eval()>.
+
+=head1 METHODS
+
+=head2 options()
+
+Returns nothing. This script has no options now. Maybe it will have
+some command lines options in the future.
+
+=cut
+
+sub options { }
+
+=head2 run()
+
+Creates a new console process.
+
+=cut
+
+sub run {
+    my $self = shift;
+    Jifty->new();
+    my $term = new Term::ReadLine 'Jifty Console';
+    my $OUT = $term->OUT || \*STDOUT;
+    my $cxt = Devel::EvalContext->new;
+    while (defined($_ = $term->readline("jifty> "))) {
+        if (/\S/) {
+            my $res = $cxt->run($_);
+            warn $@ if $@;
+            print $OUT $res, "\n" unless $@ || !defined($res);
+            $term->addhistory($_);
+        }
+    }
+}
+
+1;
+
+=head1 AUTHOR
+
+Kang-min Liu C<<gugod at gugod.org>>
+
+=cut

Modified: jifty/branches/virtual-models/lib/Jifty/Script/Plugin.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Script/Plugin.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Script/Plugin.pm	Tue Jun 12 15:48:07 2007
@@ -93,7 +93,7 @@
 package $mod_name;
 use base qw/Jifty::Plugin/;
 
-# Your plugin goes here.  If takes any configuration or arguments, you
+# Your plugin goes here.  If it takes any configuration or arguments, you
 # probably want to override L<Jifty::Plugin/init>.
 
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Script/Schema.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Script/Schema.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Script/Schema.pm	Tue Jun 12 15:48:07 2007
@@ -8,28 +8,7 @@
 use version;
 use Jifty::DBI::SchemaGenerator;
 use Jifty::Config;
-use SQL::ReservedWords;
-
-Jifty::Module::Pluggable->import(
-    require     => 1,
-    search_path => ["SQL::ReservedWords"],
-    sub_name    => '_sql_dialects',
-);
-
-our %_SQL_RESERVED          = ();
-our @_SQL_RESERVED_OVERRIDE = qw(value);
-foreach my $dialect ( 'SQL::ReservedWords', &_sql_dialects ) {
-    foreach my $word ( $dialect->words ) {
-        push @{ $_SQL_RESERVED{ lc($word) } }, $dialect->reserved_by($word);
-    }
-}
-
-# XXX TODO: QUESTIONABLE ENGINEERING DECISION
-# The SQL standard forbids columns named 'value', but just about everone on the planet
-# actually supports it. Rather than going about scaremongering, we choose
-# not to warn people about columns named 'value'
-
-delete $_SQL_RESERVED{ lc($_) } for (@_SQL_RESERVED_OVERRIDE);
+use Jifty::Schema;
 
 =head2 options
 
@@ -66,7 +45,6 @@
     $self->setup_environment();
     $self->probe_database_existence();
     $self->manage_database_existence();
-    $self->prepare_model_classes();
     if ( $self->{create_all_tables} ) {
         $self->create_all_tables();
     } elsif ( $self->{'setup_tables'} ) {
@@ -92,6 +70,20 @@
     Jifty->new( no_handle        => 1, logger_component => 'SchemaTool',) unless Jifty->class_loader;
 }
 
+=head2 schema
+
+Returns a Jifty::Schema object.
+
+=cut
+
+sub schema {
+    my $self = shift;
+
+    $self->{'SCHEMA'} ||= Jifty::Schema->new();
+    return $self->{'SCHEMA'};
+}
+
+
 =head2 print_help
 
 Prints out help for the package using pod2usage.
@@ -112,27 +104,6 @@
         if $self->{man};
 }
 
-=head2 prepare_model_classes
-
-Reads in our application class from the config file and finds all our app's models.
-
-=cut
-
-sub prepare_model_classes {
-
-    my $self = shift;
-
-    # This creates a sub "models" which when called, finds packages under
-    # the application's ::Model, requires them, and returns a list of their
-    # names.
-    Jifty::Module::Pluggable->import(
-        require     => 1,
-        except      => qr/\.#/,
-        search_path => [ "Jifty::Model", Jifty->app_class("Model") ],
-        sub_name    => 'models',
-    );
-}
-
 =head2 probe_database_existence
 
 Probes our database to see if it exists and is up to date.
@@ -141,7 +112,6 @@
 
 sub probe_database_existence {
     my $self = shift;
-
     my $no_handle = 0;
     if ( $self->{'create_database'} or $self->{'drop_database'} ) {
         $no_handle = 1;
@@ -206,7 +176,7 @@
     # Bootstrap the UUID table first
     Jifty->handle->bootstrap_uuid_table;
 
-    $self->create_tables_for_models (grep {$_->isa('Jifty::DBI::Record')}  __PACKAGE__->models );
+    $self->create_tables_for_models (grep {$_->isa('Jifty::DBI::Record')} $self->schema->models );
 
     # Update the versions in the database
     Jifty::Model::Metadata->store( application_db_version => $appv );
@@ -249,8 +219,8 @@
     my $appv = version->new( Jifty->config->framework('Database')->{'Version'} );
     my $jiftyv = version->new( $Jifty::VERSION  );
 
-    for my $model ( @models) {
 
+    for my $model ( @models) {
        # TODO XXX FIXME:
        #   This *will* try to generate SQL for abstract base classes you might
        #   stick in $AC::Model::.
@@ -260,7 +230,7 @@
         }
         $log->info("Using $model, as it appears to be new.");
 
-            $self->_check_reserved($model)
+            $self->schema->_check_reserved($model)
         unless ( $self->{'ignore_reserved'}
             or !Jifty->config->framework('Database')->{'CheckSchema'} );
 
@@ -285,9 +255,7 @@
         # Backwards combatibility -- it usd to be 'key' not 'data_key';
         eval {
             local $SIG{__WARN__} = sub { };
-            $dbv = Jifty->handle->fetch_result(
-                "SELECT value FROM _jifty_metadata WHERE key = 'jifty_db_version'"
-            );
+            $dbv = Jifty->handle->fetch_result( "SELECT value FROM _jifty_metadata WHERE key = 'jifty_db_version'");
         };
     }
 
@@ -357,7 +325,7 @@
                      $upgradeclass->versions();
     };
 
-    for my $model_class ( grep {/^\Q$baseclass\E::Model::/} __PACKAGE__->models ) {
+    for my $model_class ( grep {/^\Q$baseclass\E::Model::/} $self->schema->models ) {
 
         # We don't want to get the Collections, for example.
         next unless $model_class->isa('Jifty::DBI::Record');
@@ -378,11 +346,8 @@
                         my $renamed = $upgradeclass->just_renamed || {};
 
                         # skip it if this was dropped by a rename
-                        _exec_sql($model->drop_column_sql($col->name))
-                            unless defined $renamed
-                                ->{ $model->table }
-                                ->{'drop'}
-                                ->{ $col->name };
+                        $model->drop_column_in_db($col->name)
+                            unless defined $renamed->{ $model->table }->{'drop'}->{ $col->name };
                     };
                 }
 
@@ -392,11 +357,8 @@
                         my $renamed = $upgradeclass->just_renamed || {};
 
                         # skip it if this was added by a rename
-                        _exec_sql($model->add_column_sql($col->name))
-                            unless defined $renamed
-                                ->{ $model->table }
-                                ->{'add'}
-                                ->{ $col->name };
+                        $model->add_column_in_db($col->name)
+                            unless defined $renamed->{ $model->table }->{'add'}->{ $col->name };
                     };
                 }
             }
@@ -469,84 +431,24 @@
 sub manage_database_existence {
     my $self = shift;
 
-    my $handle = $self->_connect_to_db_for_management();
-
-        if ( $self->{'drop_database'} ) {
-        $handle->drop_database($self->{'print'} ? 'print' : 'execute');
-    }
-
-    if ( $self->{'create_database'} ) {
-        $handle->create_database($self->{'print'} ? 'print' : 'execute');
-    }
-
-    $handle->disconnect;
-
-    # If we drop and didn't re-create, then don't reconnect
-    if ( $self->{'drop_database'} and not $self->{'create_database'} ) {
-        return;
-    }
+    my $handle = Jifty::Schema->connect_to_db_for_management();
 
-    # Likewise if we didn't get a connection before, and we're just
-    # printing, the connect below will fail
-    elsif ( $self->{'print'}
-        and not( Jifty->handle and Jifty->handle->dbh->ping ) ) {
-        return;
+    if ( $self->{print} ) {
+         $handle->drop_database('print') if ( $self->{'drop_database'} ) ;
+        $handle->create_database('print') if ( $self->{'create_database'} ) ;
     } else {
-        $self->_reinit_handle();
+        $handle->drop_database('execute') if ( $self->{'drop_database'} ) ;
+        $handle->create_database('execute') if ( $self->{'create_database'} ) ;
+        $handle->disconnect;
+        $self->_reinit_handle() if ($self->{'create_database'} ) ;
     }
 }
 
-sub _connect_to_db_for_management {
-    my $handle = Jifty::Handle->new();
-
-    my $driver   = Jifty->config->framework('Database')->{'Driver'};
-
-    # Everything but the template1 database is assumed
-    my %connect_args;
-    $connect_args{'database'} = 'template1' if ( $handle->isa("Jifty::DBI::Handle::Pg") );
-    $connect_args{'database'} = ''          if ( $handle->isa("Jifty::DBI::Handle::mysql") );
-    $handle->connect(%connect_args);
-    return $handle;
-}
-
 sub _reinit_handle {
     Jifty->handle( Jifty::Handle->new() );
     Jifty->handle->connect();
 }
 
-sub __parenthesize {
-    if ( not defined $_[0] ) { return () }
-    if ( @_ == 1 )           { return $_[0] }
-    return "(" . ( join ", ", @_ ) . ")";
-}
-
-sub _classify {
-    my %dbs;
-
-    # Guess names of databases + their versions by breaking on last space,
-    # e.g., "SQL Server 7" is ("SQL Server", "7"), not ("SQL", "Server 7").
-    push @{ $dbs{ $_->[0] } }, $_->[1]
-        for map { [ split /\s+(?!.*\s)/, $_, 2 ] } @_;
-    return
-        map { join " ", $_, __parenthesize( @{ $dbs{$_} } ) } sort keys %dbs;
-}
-
-sub _check_reserved {
-    my $self  = shift;
-    my $model = shift;
-    my $log   = Log::Log4perl->get_logger("SchemaTool");
-    foreach my $col ( $model->columns ) {
-        if ( exists $_SQL_RESERVED{ lc( $col->name ) } ) {
-            $log->error(
-                      $model . ": "
-                    . $col->name
-                    . " is a reserved word in these SQL dialects: "
-                    . join( ', ',
-                    _classify( @{ $_SQL_RESERVED{ lc( $col->name ) } } ) )
-            );
-        }
-    }
-}
 
 sub _exec_sql {
     my $sql = shift;

Modified: jifty/branches/virtual-models/lib/Jifty/Script/Server.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Script/Server.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Script/Server.pm	Tue Jun 12 15:48:07 2007
@@ -93,7 +93,6 @@
         if $self->{sigready};
     Log::Log4perl->get_logger($server_class)->less_logging(3)
         if $self->{quiet};
-
     $server_class->new(port => $self->{port})->run;
 }
 

Modified: jifty/branches/virtual-models/lib/Jifty/Server.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Server.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Server.pm	Tue Jun 12 15:48:07 2007
@@ -17,8 +17,7 @@
 =cut
 
 
-use base qw/HTTP::Server::Simple::CGI/;
-use base qw/Jifty::Object/;
+use base qw/HTTP::Server::Simple::CGI Jifty::Object/;
 use File::Spec;
 use Log::Log4perl;
 use HTTP::Server::Simple;

Modified: jifty/branches/virtual-models/lib/Jifty/Util.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Util.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Util.pm	Tue Jun 12 15:48:07 2007
@@ -162,10 +162,6 @@
                 and lc($try) ne lc(File::Spec->catdir($Config::Config{bin}, "jifty"))
                 and lc($try) ne lc(File::Spec->catdir($Config::Config{scriptdir}, "jifty")) )
             {
-                #warn "root: ", File::Spec->catdir(@root);
-                #warn "bin/jifty: ", File::Spec->catdir($Config::Config{bin}, "jifty");
-                #warn "scriptdir/jifty: ", File::Spec->catdir($Config::Config{scriptdir}, "jifty");
-                #warn "try: $try";
                 return $APP_ROOT = File::Spec->catdir(@root);
             }
             pop @root;
@@ -245,11 +241,15 @@
     if ($UNIVERSAL::require::ERROR) {
         my $error = $UNIVERSAL::require::ERROR;
         $error =~ s/ at .*?\n$//;
-        
-        unless ($args{'quiet'} and $error =~ /^Can't locate/) {
+        if ($args{'quiet'} and $error =~ /^Can't locate/) {
+            return 0;
+        }
+        elsif ( $UNIVERSAL::require::ERROR !~ /^Can't locate/) {
+            die $UNIVERSAL::require::ERROR;
+        } else {
             Jifty->log->error(sprintf("$error at %s line %d\n", (caller(1))[1,2]));
+            return 0;
         }
-        return 0;
     }
 
     # If people forget the '1;' line in the dispatcher, don't eit them

Added: jifty/branches/virtual-models/lib/Jifty/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/View.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,46 @@
+package Jifty::View;
+use strict;
+use warnings;
+
+=head2 auto_send_headers
+
+Doesn't send headers if this is a subrequest (according to the current
+L<Jifty::Request>).
+
+=cut
+
+sub auto_send_headers {
+    return not Jifty->web->request->is_subrequest;
+}
+
+=head2 out_method
+
+The default output method.  Sets the content-type to C<text/html;
+charset=utf-8> unless a content type has already been set, and then
+sends a header if need be.
+
+=cut
+
+sub out_method {
+    my $r = Jifty->handler->apache;
+
+    $r->content_type || $r->content_type('text/html; charset=utf-8'); # Set up a default
+
+    unless ( $r->http_header_sent or not __PACKAGE__->auto_send_headers ) {
+        $r->send_http_header();
+    }
+
+    # We could perhaps install a new, faster out_method here that
+    # wouldn't have to keep checking whether headers have been
+    # sent and what the $r->method is.  That would require
+    # additions to the Request interface, though.
+    binmode *STDOUT;
+    if ( my ($enc) = $r->content_type =~ /charset=([\w-]+)$/ ) {
+        print STDOUT map Encode::encode($enc, $_), grep {defined} @_;
+    } else {
+        print STDOUT grep {defined} @_;
+    }
+}
+
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/CRUD.pm	Tue Jun 12 15:48:07 2007
@@ -4,34 +4,49 @@
 package Jifty::View::Declare::CRUD;
 use Jifty::View::Declare -base;
 use base 'Exporter';
-our @EXPORT = qw(object_type fragment_for get_record);
+our @EXPORT = qw(object_type fragment_for get_record current_collection);
 
+sub mount_view {
+    my ($class, $model, $vclass, $path) = @_;
+    my $caller = caller(0);
+    $model = ucfirst($model);
+    $vclass ||= $caller.'::'.$model;
+    $path ||= '/'.lc($model);
+
+    Jifty::Util->require($vclass);
+    eval qq{package $caller;
+            alias $vclass under '$path'; 1} or die $@;
+    no strict 'refs';
+    *{$vclass."::fragment_base_path"} = sub { "/$path" };
+    *{$vclass."::object_type"} = sub { $model };
+}
 
 sub object_type {
     my $self = shift;
-    return $self->package_variable('object_type')|| get('object_type');
+    return $self->package_variable('object_type') || get('object_type');
 }
 
 sub fragment_for {
-    my $self = shift;
+    my $self     = shift;
     my $fragment = shift;
-    
-    if (my $coderef = $self->can('fragment_for_'.$fragment) ) {
+
+    if ( my $coderef = $self->can( 'fragment_for_' . $fragment ) ) {
         return $coderef->($self);
     }
 
-    return $self->package_variable('fragment_for_'.$fragment)||$self->fragment_base_path ."/". $fragment;
+    return $self->package_variable( 'fragment_for_' . $fragment )
+        || $self->fragment_base_path . "/" . $fragment;
 }
 
 sub fragment_base_path {
     my $self = shift;
-    return    $self->package_variable('base_path')|| '/crud';
+    return $self->package_variable('base_path') || '/crud';
 }
 
 sub get_record {
-    my ($self, $id) = @_;
+    my ( $self, $id ) = @_;
 
-    my $record_class = Jifty->app_class("Model", $self->object_type);
+    my $record_class = Jifty->app_class( "Model", $self->object_type );
     my $record = $record_class->new();
     $record->load($id);
 
@@ -63,6 +78,13 @@
         }
 };
 
+sub display_columns {
+    my $self = shift;
+    my $action = shift;
+     return   grep { !( m/_confirm/ || lc $action->arguments->{$_}{render_as} eq 'password' ) } $action->argument_names;
+}
+
+
 template 'view' => sub {
     my $self = shift;
     my ( $object_type, $id ) = ( $self->object_type, get('id') );
@@ -74,6 +96,8 @@
 
     div {
         { class is 'crud read item inline' };
+        my @fields =$self->display_columns($update);
+        render_action( $update, \@fields, { render_mode => 'read' } );
         hyperlink(
             label   => "Edit",
             class   => "editlink",
@@ -83,11 +107,6 @@
             },
         );
 
-        my @fields = grep {
-            !( m/_confirm/
-                || lc $update->arguments->{$_}{render_as} eq 'password' )
-        } $update->argument_names;
-        render_action( $update, \@fields, { render_mode => 'read' } );
         hr {};
     };
 
@@ -109,6 +128,7 @@
     div {
         { class is "crud update item inline " . $object_type }
 
+        show('./edit_item', $update);
         div {
             { class is 'crud editlink' };
             hyperlink(
@@ -130,39 +150,53 @@
             );
         };
 
-        render_action($update);
         hr {};
         }
 };
 
-template 'list' => sub {
-    my $self = shift;
-    my ($object_type) = ( $self->object_type );
-
-    my ( $page, $fragment_for_new_item, $item_path, $search_collection )
-        = get(qw(page fragment_for_new_item item_path search_collection));
 
-    $fragment_for_new_item ||= $self->fragment_for('new_item');
-    $item_path ||= $self->fragment_for("view");
+sub current_collection {
+    my $self =shift; 
+    my ( $page, $search_collection ) = get(qw(page  search_collection));
 
-    my $collection_class
-        = Jifty->app_class( "Model", $object_type . "Collection" );
+    my $collection_class = Jifty->app_class( "Model", $self->object_type . "Collection" );
     my $search = $search_collection || Jifty->web->response->result('search');
     my $collection;
-    if ( !$search ) {
+    if ( $search ) {
+        $collection = $search;
+    } else {
         $collection = $collection_class->new();
         $collection->unlimit();
-    } else {
-        $collection = $search->content('search');
     }
 
-    $collection->set_page_info(
-        current_page => $page,
-        per_page     => 25
-    );
+    $collection->set_page_info( current_page => $page, per_page => 25 );
+
+    return $collection;    
+}
+
+template 'list' => sub {
+    my $self = shift;
+
+    my ( $page ) = get(qw(page ));
+    my $fragment_for_new_item = get('fragment_for_new_item') || $self->fragment_for('new_item');
+    my $item_path = get('item_path') || $self->fragment_for("view");
+    my $collection =  $self->current_collection();
+
+    show('./search_region');
+    show( './paging_top',    $collection, $page );
+    show( './list_items',    $collection, $item_path );
+    show( './paging_bottom', $collection, $page );
+    show( './new_item_region', $fragment_for_new_item );
+
+};
+
+private template 'search_region' => sub {
+    my $self        = shift;
+    my $object_type = $self->object_type;
+
     my $search_region = Jifty::Web::PageRegion->new(
         name => 'search',
-        path => '/__jifty/empty',
+        path => '/__jifty/empty'
     );
 
     hyperlink(
@@ -177,15 +211,26 @@
     );
 
     outs( $search_region->render );
+};
+private template 'new_item_region' => sub {
+    my $self        = shift;
+    my $fragment_for_new_item = shift;
+    my $object_type = $self->object_type;
 
-    if ( $collection->pager->last_page > 1 ) {
-        span {
-            { class is 'page-count' };
-            outs(
-                _( "Page %1 of %2", $page, $collection->pager->last_page ) );
-            }
+    if ($fragment_for_new_item) {
+        render_region(
+            name     => 'new_item',
+            path     => $fragment_for_new_item,
+            defaults => { object_type => $object_type },
+        );
     }
+};
 
+private template 'list_items' => sub {
+    my $self        = shift;
+    my $collection  = shift;
+    my $item_path   = shift;
+    my $object_type = $self->object_type;
     if ( $collection->pager->total_entries == 0 ) {
         outs( _("No items found") );
     }
@@ -201,6 +246,27 @@
         }
     };
 
+};
+
+private template 'paging_top' => sub {
+    my $self       = shift;
+    my $collection = shift;
+    my $page       = shift;
+
+    if ( $collection->pager->last_page > 1 ) {
+        span {
+            { class is 'page-count' };
+            outs(
+                _( "Page %1 of %2", $page, $collection->pager->last_page ) );
+            }
+    }
+
+};
+
+private template paging_bottom => sub {
+    my $self       = shift;
+    my $collection = shift;
+    my $page       = shift;
     div {
         { class is 'paging' };
         if ( $collection->pager->previous_page ) {
@@ -225,14 +291,13 @@
                 }
         }
     };
+};
 
-    if ($fragment_for_new_item) {
-        render_region(
-            name     => 'new_item',
-            path     => $fragment_for_new_item,
-            defaults => { object_type => $object_type },
-        );
-    }
+
+private template 'edit_item' => sub {
+    my $self = shift;
+    my $action = shift;
+    render_action($action, [$self->display_columns($action)]);
 };
 
 
@@ -245,7 +310,7 @@
 
     div {
         { class is 'crud create item inline' };
-        render_action($create);
+        show('./edit_item', $create);
 
         outs(
             Jifty->web->form->submit(
@@ -254,7 +319,8 @@
                     { submit       => $create },
                     { refresh_self => 1 },
                     {   element =>
-                            undef,  #$region->parent->get_element('div.list'),
+                            Jifty->web->current_region->parent->get_element(
+                            'div.list'),
                         append => $self->fragment_for('view'),
                         args   => {
                             object_type => $object_type,

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/Handler.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/Handler.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/Handler.pm	Tue Jun 12 15:48:07 2007
@@ -74,7 +74,8 @@
         shift;    # Turn the method into a function
         goto &Template::Declare::Tags::outs_raw;
     };
-    my $content = Template::Declare::Tags::show( $template );
+    
+    my $content = Template::Declare::Tags::show_page( $template, Jifty->web->request->arguments );
     return unless defined $content && length $content;
 
     my $r = Jifty->handler->apache;

Modified: jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/Helpers.pm	Tue Jun 12 15:48:07 2007
@@ -2,13 +2,11 @@
 use strict;
 
 package Jifty::View::Declare::Helpers;
-use base qw/Template::Declare/;
-use base qw/Exporter/;
+use base qw/Template::Declare Exporter/;
 use Template::Declare::Tags;
 
 our @EXPORT = ( qw(form hyperlink tangent redirect new_action form_submit form_return  form_next_page page wrapper request get set render_param current_user render_action render_region), @Template::Declare::Tags::EXPORT);
 
-
 =head1 NAME
 
 Jifty::View::Declare::Helpers
@@ -296,111 +294,41 @@
 =cut
 
 sub wrapper ($) {
-    my $content_code = shift;
-    my ($title) = get_current_attr(qw(title));
-
-    my $done_header;
-    my $render_header = sub {
-        no warnings qw( uninitialized redefine once );
-        $title ||= Jifty->config->framework('ApplicationName');
-
-        #   defined $title or return;
-        return if $done_header++;
-        Template::Declare->new_buffer_frame;
-        outs_raw(
-        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
-            . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">' );
-        render_header($title);
-        $done_header = Template::Declare->buffer->data;
-        Template::Declare->end_buffer_frame;
-        return '';
-    };
-
-    body {
-        div {
-            div {
-            show 'salutation'; 
-            show 'menu'; 
-        };
-            div { attr { id is 'content'};
-                div {
-                    {
-                        no warnings qw( uninitialized redefine once );
-
-                        local *is::title = sub {
-                            shift;
-                            for (@_) {
-                                if ( ref($_) eq 'CODE' ) {
-                                    Template::Declare->new_buffer_frame;
-                                    $_->();
-                                    $title .= Template::Declare->buffer->data;
-                                    Template::Declare->end_buffer_frame;
-                                } else {
-                                    $title .= Jifty->web->escape($_);
-                                }
-                            }
-                            &$render_header;
-                            my $oldt = get('title'); set(title => $title);
-                            show 'heading_in_wrapper';
-                            set(title => $oldt);
-                        };
-
-                        &_render_pre_content_hook();
-                        Jifty->web->render_messages;
-                        &$content_code;
-                        &$render_header unless ($done_header);
-                        &_render_jifty_page_detritus();
-                }
-
-                };
-            };
-        };
-    };
-    outs_raw('</html>');
-    Template::Declare->buffer->data( $done_header . Template::Declare->buffer->data );
-}
+    my $app_class = get_current_attr('PageClass') || 'View::Page';
+    delete $Template::Declare::Tags::ATTRIBUTES{ 'PageClass' };
 
-sub _render_pre_content_hook {
-    if ( Jifty->config->framework('AdminMode') ) {
-        with( class => "warning admin_mode" ), div {
-            outs( _('Alert') . ': ' );
-            outs_raw(
-                Jifty->web->tangent(
-                    label => _('Administration mode is enabled.'),
-                    url   => '/__jifty/admin/'
-                )
-            );
-            }
+    my $page_class = Jifty->app_class( $app_class );
+    $page_class = 'Jifty::View::Declare::Page'
+        unless Jifty::Util->_require( module => $page_class, quiet => 1 );
+    # XXX: fallback, this is ugly
+    Jifty::Util->require( $page_class );
+
+    my $page = $page_class->new({ content_code => shift });
+
+    my ($spa) = Jifty->find_plugin('Jifty::Plugin::SinglePage');
+
+    # XXX: spa hooks should be moved to page handlers
+    if ($spa && $page->allow_single_page) {
+	# If it's a single page app, we want to either render a
+	# wrapper and then get the region or render just the content
+        if ( !Jifty->web->current_region ) {
+	    $page->render_header;
+            $page->render_body(sub {
+                render_region( $spa->region_name,
+                    path => Jifty->web->request->path );
+            });
+	    $page->render_footer;
+        } else {
+	    $page->done_header(1);
+	    $page->render_page->();
+        }
     }
-}
-
-sub _render_jifty_page_detritus {
-
-    show('keybindings');
-    with( id => "jifty-wait-message", style => "display: none" ),
-        div { _('Loading...') };
-
-    # This is required for jifty server push.  If you maintain your own
-    # wrapper, make sure you have this as well.
-    if ( Jifty->config->framework('PubSub')->{'Enable'} && Jifty::Subs->list )
-    {
-        script { outs('new Jifty.Subs({}).start();') };
+    else {
+	$page->render_body( sub { $page->render_page->() });
+	$page->render_footer;
     }
 }
 
-=head2 render_header $title
-
-Renders an HTML "doctype", <head> and the first part of a page body. This bit isn't terribly well thought out and we're not happy with it.
-
-=cut
-
-sub render_header { 
-    my $title = shift || '';
-    $title =~ s/<.*?>//g;    # remove html
-    HTML::Entities::decode_entities($title);
-    with( title => $title ), show('header');
-}               
-                    
 
 
 

Added: jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/View/Declare/Page.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,181 @@
+package Jifty::View::Declare::Page;
+use strict;
+use warnings;
+use base qw/Template::Declare Class::Accessor::Fast/;
+use Template::Declare::Tags;
+
+=head1 NAME
+
+Jifty::View::Declare::Page
+
+=head1 DESCRIPTION
+
+This library provides page wrappers
+
+=head1 METHODS
+
+=cut
+
+use Jifty::View::Declare::Helpers;
+
+__PACKAGE__->mk_accessors(qw(content_code done_header _title));
+use constant allow_single_page => 1;
+
+sub new {
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    my ($title) = get_current_attr(qw(title));
+    $self->_title($title);
+
+    return $self;
+}
+
+=head2 render_header $title
+
+Renders an HTML "doctype", <head> and the first part of a page body. This bit isn't terribly well thought out and we're not happy with it.
+
+=cut
+
+sub render_header {
+    my $self = shift;
+    return if $self->done_header;
+
+    Template::Declare->new_buffer_frame;
+    outs_raw(
+        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
+      . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">' );
+
+    $self->_render_header($self->_title || Jifty->config->framework('ApplicationName'));
+
+    $self->done_header(Template::Declare->buffer->data);
+    Template::Declare->end_buffer_frame;
+    return '';
+};
+
+=head2 render_body $body_code
+
+Renders $body_code inside a body tag
+
+=cut
+
+sub render_body {
+    my ($self, $body_code) = @_;
+
+    body { $body_code->() };
+}
+
+=head2 render_page
+
+Renders the skeleton of the page
+
+=cut
+
+sub render_page {
+    my $self = shift;
+
+    div {
+        div {
+            show 'salutation';
+            show 'menu';
+        };
+        div {
+            attr { id is 'content' };
+            div {
+                {
+                    no warnings qw( redefine once );
+
+                    local *is::title = $self->mk_title_handler();
+                    $self->render_pre_content_hook();
+                    Jifty->web->render_messages;
+
+                    $self->content_code->();
+                    $self->render_header();
+
+                    $self->render_jifty_page_detritus();
+                }
+
+            };
+        };
+    };
+}
+
+=head2 mk_title_handler
+
+Returns a coderef that will make headers for each thing passed to it
+
+=cut
+
+sub mk_title_handler {
+    my $self = shift;
+    return sub {
+        shift;
+        for (@_) {
+	    no warnings qw( uninitialized );
+            if ( ref($_) eq 'CODE' ) {
+                Template::Declare->new_buffer_frame;
+                $_->();
+                $self->_title(
+                    $self->_title . Template::Declare->buffer->data );
+                Template::Declare->end_buffer_frame;
+            } else {
+                $self->_title( $self->_title . Jifty->web->escape($_) );
+            }
+        }
+        $self->render_header;
+	$self->render_title();
+    };
+}
+
+sub render_title {
+    my $self = shift;
+    my $oldt = get('title');
+    set( title => $self->_title );
+    show 'heading_in_wrapper';
+    set( title => $oldt );
+}
+
+sub render_footer {
+    my $self = shift;
+    outs_raw('</html>');
+    Template::Declare->buffer->data( $self->done_header . Template::Declare->buffer->data );
+}
+
+
+sub render_pre_content_hook {
+    if ( Jifty->config->framework('AdminMode') ) {
+        with( class => "warning admin_mode" ), div {
+            outs( _('Alert') . ': ' );
+            outs_raw(
+                Jifty->web->tangent(
+                    label => _('Administration mode is enabled.'),
+                    url   => '/__jifty/admin/'
+                )
+            );
+            }
+    }
+}
+
+sub render_jifty_page_detritus {
+
+    show('keybindings');
+    with( id => "jifty-wait-message", style => "display: none" ),
+        div { _('Loading...') };
+
+    # This is required for jifty server push.  If you maintain your own
+    # wrapper, make sure you have this as well.
+    if ( Jifty->config->framework('PubSub')->{'Enable'} && Jifty::Subs->list )
+    {
+        script { outs('new Jifty.Subs({}).start();') };
+    }
+}
+
+sub _render_header { 
+    my $self = shift;
+    my $title = shift || '';
+    $title =~ s/<.*?>//g;    # remove html
+    HTML::Entities::decode_entities($title);
+    with( title => $title ), show('header');
+}
+
+1;

Modified: jifty/branches/virtual-models/lib/Jifty/View/Mason/Handler.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/View/Mason/Handler.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/View/Mason/Handler.pm	Tue Jun 12 15:48:07 2007
@@ -23,7 +23,7 @@
 use Encode qw();
 
 use Class::Container;
-use base qw(Class::Container);
+use base qw(Jifty::View Class::Container);
 
 use HTML::Mason::MethodMaker
     ( read_write => [ qw( interp ) ] );
@@ -56,7 +56,7 @@
 
     my %p = @_ || $package->config;
     my $self = $package->SUPER::new( request_class => 'HTML::Mason::Request::Jifty',
-                                     out_method => \&out_method,
+                                     out_method => $package->can('out_method'),
                                      %p );
     $self->interp->compiler->add_allowed_globals('$r');
     $self->interp->set_escape( h => \&escape_utf8 );
@@ -117,37 +117,6 @@
     return %config;
 }
 
-=head2 out_method
-
-The default output method.  Sets the content-type to C<text/html;
-charset=utf-8> unless a content type has already been set, and then
-sends a header if need be.
-
-=cut
-
-sub out_method {
-    my $m = HTML::Mason::Request->instance;
-    my $r = Jifty->handler->apache;
-
-    $r->content_type || $r->content_type('text/html; charset=utf-8'); # Set up a default
-
-    unless ( $r->http_header_sent or not $m->auto_send_headers ) {
-        $r->send_http_header();
-    }
-
-    # We could perhaps install a new, faster out_method here that
-    # wouldn't have to keep checking whether headers have been
-    # sent and what the $r->method is.  That would require
-    # additions to the Request interface, though.
-    binmode *STDOUT;
-    if ( my ($enc) = $r->content_type =~ /charset=([\w-]+)$/ ) {
-        print STDOUT map Encode::encode($enc, $_), grep {defined} @_;
-    } else {
-        print STDOUT grep {defined} @_;
-    }
-}
-
-
 =head2 escape_utf8 SCALARREF
 
 Does a css-busting but minimalist escaping of whatever html you're passing in.
@@ -280,7 +249,7 @@
 =cut
 
 sub auto_send_headers {
-    return not Jifty->web->request->is_subrequest;
+    Jifty::View->auto_send_headers;
 }
 
 =head2 exec

Modified: jifty/branches/virtual-models/lib/Jifty/Web.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web.pm	Tue Jun 12 15:48:07 2007
@@ -27,7 +27,7 @@
 
 __PACKAGE__->mk_classdata($_)
     for qw(cached_css        cached_css_digest        cached_css_time
-           cached_javascript cached_javascript_digest cached_javascript_time javascript_libs);
+           javascript_libs);
 
 __PACKAGE__->javascript_libs([qw(
     jsan/JSAN.js
@@ -71,6 +71,8 @@
     css_browser_selector.js
 )]);
 
+use Jifty::DBI::Class::Trigger;
+
 =head1 METHODS
 
 =head3 new
@@ -106,8 +108,8 @@
 =cut
 
 sub out {
-    Carp::cluck unless defined $_[0]->mason;
-    shift->mason->out(@_);
+    my $m = shift->mason;
+    $m ? $m->out(@_) : Jifty::View::out_method(@_);
 }
 
 
@@ -299,18 +301,65 @@
 
 sub handle_request {
     my $self = shift;
-    die _( "No request to handle" ) unless Jifty->web->request;
+    die _("No request to handle") unless Jifty->web->request;
+
+    my ( $valid_actions, $denied_actions )
+        = $self->_validate_request_actions();
+
+    # In the case where we have a continuation and want to redirect
+    if ( $self->request->continuation_path && Jifty->web->request->argument('_webservice_redirect') ) {
+        # for continuation - perform internal redirect under webservices
+	$self->webservices_redirect($self->request->continuation_path);
+        return;
+    }
+
+    $self->request->save_continuation;
+
+    unless ( $self->request->just_validating ) {
+        $self->_process_valid_actions($valid_actions);
+        $self->_process_denied_actions($denied_actions);
+    }
+
+    # If there's a continuation call, don't do the rest of this
+    return if $self->response->success and $self->request->call_continuation;
+
+    $self->redirect if $self->redirect_required;
+    $self->request->do_mapping;
+}
+
+sub _process_denied_actions {
+    my $self           = shift;
+    my $denied_actions = shift;
+
+    for my $request_action (@$denied_actions) {
+        my $action = $self->new_action_from_request($request_action);
+        $action->deny( "Access Denied for " . ref($action) );
+        $self->response->result( $action->moniker => $action->result );
+    }
+}
 
+sub _validate_request_actions {
+        my $self = shift;
+        
     my @valid_actions;
+    my @denied_actions;
+
+
     for my $request_action ( $self->request->actions ) {
-        $self->log->debug("Found action ".$request_action->class . " " . $request_action->moniker);
+        $self->log->debug( "Found action "
+                . $request_action->class . " "
+                . $request_action->moniker );
         next unless $request_action->active;
         next if $request_action->has_run;
-        unless ( Jifty->api->is_allowed( $request_action->class ) ) {
-            $self->log->warn( "Attempt to call denied action '"
-                    . $request_action->class
-                    . "'" );
-            next;
+        unless ( $self->request->just_validating ) {
+            unless ( Jifty->api->is_allowed( $request_action->class ) ) {
+                $self->log->warn( "Attempt to call denied action '"
+                        . $request_action->class
+                        . "'" );
+                Carp::cluck;
+                push @denied_actions, $request_action;
+                next;
+            }
         }
 
         # Make sure we can instantiate the action
@@ -321,16 +370,21 @@
         # Try validating -- note that this is just the first pass; as
         # actions are run, they may fill in values which alter
         # validation of later actions
-        $self->log->debug("Validating action ".ref($action). " ".$action->moniker);
+        $self->log->debug( "Validating action " . ref($action) . " " . $action->moniker );
         $self->response->result( $action->moniker => $action->result );
         $action->validate;
 
         push @valid_actions, $request_action;
     }
-    $self->request->save_continuation;
+    
+    return (\@valid_actions, \@denied_actions);
 
-    unless ( $self->request->just_validating ) {
-        for my $request_action (@valid_actions) {
+}
+
+sub _process_valid_actions {
+    my  $self = shift;
+    my $valid_actions = shift;
+        for my $request_action (@$valid_actions) {
 
             # Pull the action out of the request (again, since
             # mappings may have affected parameters).  This
@@ -339,20 +393,26 @@
             # actions (Jifty::Request::Mapper)
             my $action = $self->new_action_from_request($request_action);
             next unless $action;
-            if ($request_action->modified) {
+            if ( $request_action->modified ) {
+
                 # If the request's action was changed, re-validate
-                $action->result(Jifty::Result->new);
-                $action->result->action_class(ref $action);
-                $self->response->result( $action->moniker => $action->result );
-                $self->log->debug("Re-validating action ".ref($action). " ".$action->moniker);
+                $action->result( Jifty::Result->new );
+                $action->result->action_class( ref $action );
+                $self->response->result(
+                    $action->moniker => $action->result );
+                $self->log->debug( "Re-validating action "
+                        . ref($action) . " "
+                        . $action->moniker );
                 next unless $action->validate;
             }
 
-            $self->log->debug("Running action ".ref($action). " ".$action->moniker);
+            $self->log->debug(
+                "Running action " . ref($action) . " " . $action->moniker );
             eval { $action->run; };
             $request_action->has_run(1);
 
             if ( my $err = $@ ) {
+
                 # Poor man's exception propagation; we need to get
                 # "LAST RULE" and "ABORT" exceptions back up to the
                 # dispatcher.  This is specifically for redirects from
@@ -362,7 +422,8 @@
                 $action->result->error(
                     Jifty->config->framework("DevelMode")
                     ? $err
-                    : _("There was an error completing the request.  Please try again later.")
+                    : _("There was an error completing the request.  Please try again later."
+                    )
                 );
             }
 
@@ -370,15 +431,8 @@
             # may have yielded.
             $self->request->do_mapping;
         }
-    }
-
-    # If there's a continuation call, don't do the rest of this
-    return if $self->response->success and $self->request->call_continuation;
-
-    $self->redirect if $self->redirect_required;
-    $self->request->do_mapping;
-}
 
+    }
 =head3 request [VALUE]
 
 Gets or sets the current L<Jifty::Request> object.
@@ -593,22 +647,53 @@
     }
 }
 
+=head3 webservices_redirect [TO]
+
+Handle redirection inside webservices call.  This is meant to be
+hooked by plugin that knows what to do with it.
+
+=cut
+
+sub webservices_redirect {
+    my ( $self, $to ) = @_;
+    # XXX: move to singlepage plugin
+    my ($spa) = Jifty->find_plugin('Jifty::Plugin::SinglePage') or return;
+
+    Jifty->web->request->remove_state_variable( 'region-'.$spa->region_name );
+    Jifty->web->request->add_fragment(
+        name      => $spa->region_name,
+        path      => $to,
+        arguments => {},
+        wrapper   => 0
+    );
+}
+
 =head3 redirect [TO]
 
 Redirect to the next page. If you pass this method a parameter, it
 redirects to that URL rather than B<next_page>.
 
 It creates a continuation of where you want to be, and then calls it.
-If you want to redirect to a parge with parameters, pass in a
+If you want to redirect to a page with parameters, pass in a
 L<Jifty::Web::Form::Clickable> object.
 
 =cut
 
 sub redirect {
     my $self = shift;
-    my $page = shift || $self->next_page || $self->request->path;
-    $page = Jifty::Web::Form::Clickable->new( url => $page )
-      unless ref $page and $page->isa("Jifty::Web::Form::Clickable");
+    my $redir_to = shift || $self->next_page || $self->request->path;
+
+    
+    my $page;
+
+    if ( ref $redir_to and $redir_to->isa("Jifty::Web::Form::Clickable")) {
+        $page = $redir_to;
+    } else {
+
+        $page = Jifty::Web::Form::Clickable->new();
+        #We set this after creation to ensure that plugins that massage clickables don't impact us
+        $page->url($redir_to );
+    }
 
     carp "Don't include GET parameters in the redirect URL -- use a Jifty::Web::Form::Clickable instead.  See L<Jifty::Web/redirect>" if $page->url =~ /\?/;
 
@@ -619,6 +704,9 @@
 
     # To submit a Jifty::Action::Redirect, we don't need to serialize a continuation,
     # unlike any other kind of actions.
+
+    my $redirect_to_url = '' ;
+
     if (  (grep { not $_->action_class->isa('Jifty::Action::Redirect') }
                 values %{ { $self->response->results } })
         or $self->request->state_variables
@@ -655,17 +743,34 @@
             response => $self->response,
             parent   => $self->request->continuation,
         );
-        $page = $page->url."?J:RETURN=" . $cont->id;
+        $redirect_to_url = $page->url."?J:RETURN=" . $cont->id;
     } else {
-        $page = $page->complete_url;
+        $redirect_to_url = $page->complete_url;
     }
-    $self->_redirect($page);
+    $self->_redirect($redirect_to_url);
 }
 
 sub _redirect {
     my $self = shift;
     my ($page) = @_;
 
+    # It's an experimental feature to support redirect within a
+    # region.
+    if ($self->current_region) { 
+        # If we're within a region stack, we don't really want to
+        # redirect. We want to redispatch.  Also reset the things
+        # applied on beofre.
+        local $self->{navigation} = undef;
+        local $self->{page_navigation} = undef;
+        $self->replace_current_region($page);
+        Jifty::Dispatcher::_abort;
+        return;
+    }
+
+    if (my $redir = Jifty->web->request->argument('_webservice_redirect')) {
+	push @$redir, $page;
+	return;
+    }
     # $page can't lead with // or it assumes it's a URI scheme.
     $page =~ s{^/+}{/};
 
@@ -678,7 +783,7 @@
 
     my $apache = Jifty->handler->apache;
 
-    $self->log->debug("Redirecting to $page");
+    $self->log->debug("Execing redirect to $page");
     # Headers..
     $apache->header_out( Location => $page );
     $apache->header_out( Status => 302 );
@@ -721,6 +826,10 @@
 sub tangent {
     my $self = shift;
 
+    if (@_ == 1  ) {
+        Jifty->log->error("Jifty::Web->tangent takes a paramhash. Perhaps you passed '".$_[0]."' , rather than 'url => ".$_[0]."'");
+        die; 
+    }
     my $clickable = Jifty::Web::Form::Clickable->new(
         returns        => { },
         preserve_state => 1,
@@ -969,19 +1078,18 @@
 
 sub include_css {
     my $self = shift;
-    
-    if ( Jifty->config->framework('DevelMode') ) {
+    my ($ccjs) = Jifty->find_plugin('Jifty::Plugin::CompressedCSSandJS');
+    if ( $ccjs && $ccjs->css_enabled ) {
+        $self->generate_css;
         $self->out(
-            '<link rel="stylesheet" type="text/css" '
-            . 'href="/static/css/main.css" />'
+            '<link rel="stylesheet" type="text/css" href="/__jifty/css/'
+            . __PACKAGE__->cached_css_digest . '.css" />'
         );
     }
     else {
-        $self->generate_css;
-    
         $self->out(
-            '<link rel="stylesheet" type="text/css" href="/__jifty/css/'
-            . __PACKAGE__->cached_css_digest . '.css" />'
+            '<link rel="stylesheet" type="text/css" '
+            . 'href="/static/css/main.css" />'
         );
     }
     
@@ -997,7 +1105,7 @@
 
 sub generate_css {
     my $self = shift;
-    
+
     if (not defined __PACKAGE__->cached_css_digest
             or Jifty->config->framework('DevelMode'))
     {
@@ -1050,15 +1158,17 @@
 
 However if you want to include other javascript libraries you need to
 add them to the javascript_libs array of your application.  Do this in
-the C<start> sub of your main application class.  For example if your application is Foo then in L<lib/Foo.pm>
+the C<start> sub of your main application class.  For example if your
+application is Foo then in L<lib/Foo.pm>
 
  sub start {
-   Jifty->web->javascript_libs([
- 			       @{ Jifty->web->javascript_libs },
- 			       "yourJavascriptLib.js",
- 			      ]);
+     Jifty->web->add_javascript(qw( jslib1.js jslib2.js ) );
  }
 
+The L<add_javascript> method will append the files to javascript_libs.
+If you need a different order, you'll have to massage javascript_libs
+directly.
+
 Jifty will look for javascript libraries under share/web/static/js/ by
 default.
 
@@ -1066,93 +1176,32 @@
 
 sub include_javascript {
     my $self  = shift;
-    
-    if ( Jifty->config->framework('DevelMode') ) {
-        for my $file ( @{ __PACKAGE__->javascript_libs } ) {
-            $self->out(
-                qq[<script type="text/javascript" src="/static/js/$file"></script>\n]
-            );
-        }
-    }
-    else {
-        $self->generate_javascript;
-    
+
+    # if there's no trigger, 0 is returned.  if aborted/handled, undef
+    # is returned.
+    defined $self->call_trigger('include_javascript', @_) or return '';
+
+    for my $file ( @{ __PACKAGE__->javascript_libs } ) {
         $self->out(
-            qq[<script type="text/javascript" src="/__jifty/js/]
-            . __PACKAGE__->cached_javascript_digest . qq[.js"></script>]
+            qq[<script type="text/javascript" src="/static/js/$file"></script>\n]
         );
     }
-    
+
     return '';
 }
 
-=head3 generate_javascript
+=head3 add_javascript FILE1, FILE2, ...
 
-Checks if the compressed JS is generated, and if it isn't, generates
-and caches it.
+Pushes files onto C<Jifty->web->javascript_libs>
 
 =cut
 
-sub generate_javascript {
+sub add_javascript {
     my $self = shift;
-    
-    if (not defined __PACKAGE__->cached_javascript_digest
-            or Jifty->config->framework('DevelMode'))
-    {
-        Jifty->log->debug("Generating JS...");
-        
-        my @roots = (
-            Jifty::Util->absolute_path(
-                File::Spec->catdir(
-                    Jifty->config->framework('Web')->{'StaticRoot'},
-                    'js'
-                )
-            ),
-
-            Jifty::Util->absolute_path(
-                File::Spec->catdir(
-                    Jifty->config->framework('Web')->{'DefaultStaticRoot'},
-                    'js'
-                )
-            ),
-        );
-        
-        my $js = "";
-
-        for my $file ( @{ __PACKAGE__->javascript_libs } ) {
-            my $include;
-        
-            for my $root (@roots) {
-                my @spec = File::Spec->splitpath( $root, 1 );
-                my $path = File::Spec->catpath( @spec[0,1], $file );
-                
-                if ( -e $path ) {
-                    $include = $path;
-                    last;
-                }
-            }
-
-            if ( defined $include ) {
-                my $fh;
-
-                if ( open $fh, '<', $include ) {
-                    $js .= "/* Including '$file' */\n\n";
-                    $js .= $_ while <$fh>;
-                    $js .= "\n/* End of '$file' */\n\n";
-                }
-                else {
-                    $js .= "\n/* Unable to open '$file': $! */\n";
-                }
-            }
-            else {
-                $js .= "\n/* Unable to find '$file' */\n";
-            }
-        }
-
-        __PACKAGE__->cached_javascript( $js );
-        __PACKAGE__->cached_javascript_digest( md5_hex( $js ) );
-        __PACKAGE__->cached_javascript_time( time );
-    }
+    Jifty->web->javascript_libs([
+        @{ Jifty->web->javascript_libs },
+        @_
+    ]);
 }
 
 =head2 STATE VARIABLES
@@ -1259,6 +1308,22 @@
     $region->render;
 }
 
+
+=head3 replace_current_region PATH
+
+Replaces the current region with a new region and renders it Returns undef if there's no current region
+
+=cut
+
+sub replace_current_region {
+    my $self = shift;
+    my $path = shift;
+    return undef unless (my $region = $self->current_region);
+    $region->force_path($path);
+    $region->render;
+}
+
+
 =head3 current_region
 
 Returns the name of the current L<Jifty::Web::PageRegion>, or undef if

Modified: jifty/branches/virtual-models/lib/Jifty/Web/Form.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web/Form.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web/Form.pm	Tue Jun 12 15:48:07 2007
@@ -217,8 +217,7 @@
 
 sub submit {
     my $self = shift;
-
-    my $button = Jifty::Web::Form::Clickable->new(submit => undef, @_)->generate;
+    my $button = Jifty::Web::Form::Clickable->new(submit => undef, _form => $self, @_)->generate;
     Jifty->web->out(qq{<div class="submit_button">});
     $button->render_widget;
     Jifty->web->out(qq{</div>});

Modified: jifty/branches/virtual-models/lib/Jifty/Web/Form/Clickable.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web/Form/Clickable.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web/Form/Clickable.pm	Tue Jun 12 15:48:07 2007
@@ -2,6 +2,7 @@
 use strict;
 
 package Jifty::Web::Form::Clickable;
+use Class::Trigger;
 
 =head1 NAME
 
@@ -143,6 +144,8 @@
         @_,
     );
 
+    $class->call_trigger('before_new', \%args);
+
     $args{render_as_button} = delete $args{as_button};
     $args{render_as_link}   = delete $args{as_link};
 
@@ -452,11 +455,25 @@
           escape_label => $self->escape_label,
           url          => $self->complete_url,
           target       => $self->target,
+          continuation => $self->_continuation,
           @_ }
     );
     return $link;
 }
 
+sub _continuation {
+    # continuation info used by the update() call on client side
+    my $self = shift;
+    if ($self->call) {
+	return { 'type' => 'call', id => $self->call };
+    }
+    if ($self->returns) {
+	return { 'create' => $self->url };
+    }
+
+    return {};
+}
+
 =head2 as_button
 
 Returns the clickable as a L<Jifty::Web::Form::Field::InlineButton>,
@@ -473,6 +490,7 @@
     my $field = Jifty::Web::Form::Field->new(
         { %$args,
           type => 'InlineButton',
+          continuation => $self->_continuation,
           @_ }
     );
     my %parameters = $self->post_parameters;

Modified: jifty/branches/virtual-models/lib/Jifty/Web/Form/Element.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web/Form/Element.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web/Form/Element.pm	Tue Jun 12 15:48:07 2007
@@ -149,8 +149,8 @@
 
 =cut
 
-sub accessors { shift->handlers, qw(class key_binding key_binding_label id label tooltip) }
-__PACKAGE__->mk_accessors(qw(_onclick class key_binding key_binding_label id label tooltip));
+sub accessors { shift->handlers, qw(class key_binding key_binding_label id label tooltip continuation) }
+__PACKAGE__->mk_accessors(qw(_onclick class key_binding key_binding_label id label tooltip continuation));
 
 =head2 new PARAMHASH OVERRIDE
 
@@ -236,7 +236,10 @@
         next unless $value;
 
         my @fragments;
-        my %actions;    # Maps actions => disable?
+            # if $actions is undef, that means we're submitting _all_ actions in the clickable
+            # if $actions is defined but empty, that means we're submitting no actions
+            # if $actions is not empty, we're submitting those actions
+        my $actions = {};    # Maps actions => disable?
         my $confirm;
         my $beforeclick;
 
@@ -245,11 +248,14 @@
             my %args;
 
             # Submit action
-            if ( $hook->{submit} ) {
-                my $disable = exists $hook->{disable} ? $hook->{disable} : 1;
+          
+            
+            if ( exists $hook->{submit} ) {
+                $actions = undef;
+                my $disable_form_on_click = exists $hook->{disable} ? $hook->{disable} : 1;
                 # Normalize to 1/0 to pass to JS
-                $disable = $disable ? 1 : 0;
-                $actions{$_} = $disable for (@{ $hook->{submit} }); 
+                $disable_form_on_click = $disable_form_on_click ? 1 : 0;
+                $actions->{$_} = $disable_form_on_click for (@{ $hook->{submit} || [] }); 
             }
 
             $hook->{region} ||= Jifty->web->qualified_region;
@@ -317,9 +323,10 @@
         }
 
         my $string = join ";", (grep {not ref $_} (ref $value eq "ARRAY" ? @{$value} : ($value)));
-        if (@fragments or %actions) {
+        if (@fragments or (!$actions || %$actions)) {
 
-            my $update = Jifty->web->escape("update( ". Jifty::JSON::objToJson( {actions => \%actions, fragments => \@fragments }, {singlequote => 1}) .", this );");
+            my $update = Jifty->web->escape("update( ". Jifty::JSON::objToJson( {actions => $actions, fragments => \@fragments, continuation => $self->continuation }, {singlequote => 1}) .", this );");
+            $string .= 'if(event.ctrlKey) return true; ';
             $string .= $self->javascript_preempt ? "return $update" : "$update; return true;";
         }
         if ($confirm) {

Modified: jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Web/PageRegion.pm	Tue Jun 12 15:48:07 2007
@@ -215,7 +215,7 @@
             $self->argument($2 => $value);
         }
         if ($key =~ /^region-(.*)$/ and $1 eq $self->qualified_name and $value ne $self->default_path) {
-            $self->path($value);
+            $self->path(URI::Escape::uri_unescape($value));
         }
 
         # We should always inherit the state variables from the uplevel request.
@@ -298,7 +298,7 @@
 sub render_as_subrequest {
     my ($self, $out_method, $arguments, $enable_actions) = @_;
 
-    my $orig_out = Jifty->handler->mason->interp->out_method || \&Jifty::View::Mason::Handler::out_method;
+    my $orig_out = Jifty->handler->mason->interp->out_method || Jifty::View->can('out_method');
 
     Jifty->handler->mason->interp->out_method($out_method);
 
@@ -310,6 +310,17 @@
     $subrequest->path( $self->path );
     $subrequest->top_request( Jifty->web->request->top_request );
 
+    if ($self->path =~ m/\?/) {
+	# XXX: this only happens if we are redirect within region AND
+	# with continuation, which is already taken care of by the
+	# clone.
+	my ($path, $arg) = split(/\?/, $self->path, 2);
+	$subrequest->path( $path );
+	my %args = (map { split /=/, $_ } split /&/, $arg);
+	if ($args{'J:C'}) {
+	    $subrequest->continuation($args{'J:C'});
+	}
+    }
     # Remove all of the actions
     unless ($enable_actions) {
 	$_->active(0) for ($subrequest->actions);

Added: jifty/branches/virtual-models/plugins/CodePress/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/Makefile.PL	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,8 @@
+use inc::Module::Install;
+name('Jifty-Plugin-CodePress');
+version('0.02');
+requires('Jifty' => '0.70422');
+
+install_share;
+
+WriteAll;

Added: jifty/branches/virtual-models/plugins/CodePress/doc/index.html
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/doc/index.html	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,436 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+<head>
+	<title>CodePress - Real Time Syntax Highlighting Editor written in JavaScript</title>
+
+	<style>
+	body {color:#000;background-color:white;font:15px georgia, "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif; letter-spacing:0.01em;margin:15px;}
+	p {margin:0 0 15px 0;}
+	a,a:visited {color:#7f0055;}
+	select {background:#ffffe1;}
+	button {margin-top:5px;}
+	button.actions {width:171px;font-family:arial;}
+	h1 {color:#7f0055;margin:0;padding:0;font-size:42px;font-weight:normal;}
+	h1 a {text-decoration:none;}
+	h2 {margin:0;}
+	h2 a {text-decoration:none;font-weight:normal;font-size:22px;color:black !important;}
+	h3 {font-size:20px;font-weight:normal;padding:0;margin:25px 0 5px 0;color:#7f0055;font-weight:bold;border-bottom:2px dotted #d8d8d8;}
+	h4 {font-size:18px;font-weight:normal;z-index:0;}	
+	code {color:#0080c0;font-size:13px;font-weight:bold;}
+	ol, ul {padding:5px 0 5px 25px;margin:0;}
+	ol li, ul li {margin:8px 0 8px 0;}
+	
+	#logo {text-align:center;background-color:#d6d6d6;padding:10px;-moz-border-radius:5px;border:1px solid silver;}
+	#container {width:700px;margin:20px auto;padding:25px;border:3px solid #d9d9d9;-moz-border-radius:10px;background:#f8f8f8;}
+	#languages {margin:5px 0;}
+	
+	#menu {width:100%;background:#7f0055;-moz-border-radius:4px;}
+	#menu a {font:bold 17px georgia;color:white;padding:4px;display:block;border-left:1px solid white;text-align:center;text-decoration:none;}
+	#menu a:hover {background:#b9669e;} 
+
+	.hidden-code {display:none;}
+	.copycode {border:1px dotted gray;padding:10px;background:white;font-family:monospace;color:gray}
+	</style>
+
+</head>
+
+<body>
+<div id="container">
+
+<div id="logo">
+	<h1><a href="http://codepress.org/">CodePress</a></h1>
+	<h2><a href="http://codepress.org/">Online Real Time Syntax Highlighting Editor</a></h2>
+</div>
+
+<br />
+
+<table cellpadding="0" cellspacing="0" id="menu">
+<tr>
+	<td>
+		<a href="http://www.codepress.org/index.php">Home/Download</a>
+	</td>
+	<td>
+		<a href="http://www.codepress.org/install.php">&nbsp;&nbsp;Install&nbsp;&nbsp;</a>
+	</td>
+	<td>
+		<a href="http://www.codepress.org/to-do.php">&nbsp;&nbsp;To-do&nbsp;&nbsp;</a>
+	</td>
+	<td>
+		<a href="http://www.codepress.org/about.php" id="about">&nbsp;&nbsp;About&nbsp;&nbsp;</a>
+	</td>
+</tr>
+</table>
+
+<h4>
+	CodePress is web-based source code editor with syntax highlighting written in JavaScript that colors text in real time while it's being typed in the browser.
+</h4>
+	
+<p>
+	Go to <strong><a href="http://codepress.org/">http://codepress.org/</a></strong> for updates.
+</p>
+
+<h3>Demo</h3>
+<div id="languages">
+	<em>choose example in:</em> 
+	<button onclick="cp1.edit('cp-php','php')">PHP</button> 
+	<button onclick="cp1.edit('cp-javascript','javascript')">JavaScript</button> 
+	<button onclick="cp1.edit('cp-java','java')">Java</button>
+	<button onclick="cp1.edit('cp-perl','perl')">Perl</button>
+	<button onclick="cp1.edit('cp-sql','sql')">SQL</button>	
+	<button onclick="cp1.edit('cp-html','html')">HTML</button> 
+	<button onclick="cp1.edit('cp-css','css')">CSS</button> 	
+</div>
+
+<textarea id="cp1" class="codepress php" style="width:700px;height:300px;" wrap="off">
+<?php
+// Very simple implementation of server side script
+
+if(isset($_GET['file'])) {
+	$file = basename($_GET['file']);
+	$full_file = $path['server'].'/'.$path['webdocs'].'/'.$path['files']."/".$file;
+	if(file_exists($full_file)) {
+		$code = file_get_contents($full_file);
+		$code = preg_replace("/>/","&amp;gt;",$code);
+		$code = preg_replace("/</","&amp;lt;",$code);
+		$language = getLanguage($file);
+	}
+}
+?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<title>CodePress - Real Time Syntax Highlighting Editor written in JavaScript</title>
+	<link type="text/css" href="languages/codepress-<?=$language?>.css" rel="stylesheet" id="cp-lang-style" />
+	<script type="text/javascript" src="codepress.js"></script>
+	<script type="text/javascript">
+		CodePress.language = '<?=$language?>';
+	</script>
+</head>
+<body id="ffedt"><pre id="ieedt"><?=$code?></pre></body>
+</html>
+</textarea>
+
+<br /><br />
+
+<textarea id="codepress2" class="codepress javascript linenumbers-off" style="width:700px;height:200px;" wrap="off">
+//set language
+this.setLanguage = function() {
+	if(arguments[0]) {
+		language = (typeof(Content.languages[arguments[0]])!='undefined') ? arguments[0] : this.setLanguage();
+		cpLanguage.innerHTML = Content.languages[language].name;
+		if(cpBody.document.designMode=='on') cpBody.document.designMode = 'off';
+		CodePress.loadScript(cpBody.document, '../languages/'+language+'.js', function () { cpBody.CodePress.syntaxHighlight('init'); })
+		cpBody.document.getElementById('cp-lang-style').href = '../languages/'+language+'.css';
+		this.hideMenu();
+	}
+	else {
+		var extension = filename.replace(/.*\.([^\.]+)$/,'$1');
+		var aux = false;
+		for(lang in Content.languages) {
+			extensions = ','+Content.languages[lang].extensions+',';
+			if(extensions.match(','+extension+',')) aux = lang;
+		}
+		language = (aux) ? aux : 'generic';
+	}
+}
+</textarea>
+
+<p>
+	<button class="actions" onclick="alert(codepress2.getCode())">get code from editor</button>
+	<button class="actions" onclick="codepress2.toggleEditor()">turn on/off CodePress</button>
+	<button class="actions" onclick="codepress2.toggleLineNumbers()">show/hide line numbers</button>
+	<button class="actions" onclick="codepress2.toggleAutoComplete()">turn on/off auto-complete</button>
+	<button class="actions" onclick="codepress2.toggleReadOnly()">turn on/off read only</button>
+</p>
+
+
+
+<h3>Installation</h3>
+<ol>
+	<li>
+		<p>
+			<a href="http://codepress.org/">Download</a> and uncompress CodePress under a directory inside your webserver.<br>
+			Example:<strong> http://yourserver/codepress/</strong><br />
+			Since CodePress is pure JavaScript and HTML, you can also test it without a webserver.
+		</p>
+	</li>
+	<li>
+		<p>
+			Insert CodePress script somewhere in your page inside the <code>&lt;head&gt;</code> or above the <code>&lt;/body&gt;</code> tag.
+		</p>
+	
+		<p class="copycode">
+			&lt;script src="/codepress/codepress.js" type="text/javascript"&gt;&lt;/script&gt;
+		</p>
+	</li>
+	
+	<li>
+		<p>
+			Add the <code>&lt;textarea&gt;</code> tag to the place on your page you want CodePress to appear. CodePress will inherit the width and height of your textarea.
+			When the page loads, it will automatically replace your textarea with a CodePress window.
+		</p>
+		<p class="copycode">
+			&lt;textarea id="myCpWindow" class="codepress javascript linenumbers-off"&gt;<br />
+			&nbsp;&nbsp;&nbsp;// your code here<br />
+			&lt;/textarea&gt;
+		</p>
+		<ul>
+			<li>
+				The <code>javascript</code> portion of the class="" means that the language being edited is JavaScript.
+			</li>
+			<li>
+				The <code>codepress</code> portion of the class="" is mandatory and indicates a textarea to be replaced for a CodePress window.
+			</li>
+			<li>
+				Other class options are <code>linenumbers-off</code>, <code>autocomplete-off</code> and <code>readonly-on</code>.
+			</li>			
+			<li>
+				Careful not to use the same id for two different CodePress windows (<code>&lt;textarea id="<strong>xx</strong>"...&gt;</code>)
+			</li>
+		</ul>
+
+	</li>
+</ol>
+
+<h3>You also can...</h3>
+<ol>
+	<li>
+		Open/edit code from a different textarea.<br />
+		Example: <code>textarea_id.edit('other_textarea_id','language')</code><br>
+	</li>
+	<li>
+		Get code from CodePress window.<br />
+		Example: <code>textarea_id.getCode()</code><br>
+	</li>
+	<li>
+		Turn on/off CodePress editor and return to the regular textarea.<br />
+		Example: <code>textarea_id.toggleEditor()</code><br>
+	</li>
+	<li>
+		Turn on/off line numbers.<br />
+		Example: <code>textarea_id.toggleLineNumbers()</code><br>
+	</li>
+	<li>
+		Turn on/off read only.<br />
+		Example: <code>textarea_id.toggleReadOnly()</code><br>
+	</li>
+	<li>
+		Turn on/off auto-complete.<br />
+		Example: <code>textarea_id.toggleAutoComplete()</code><br>
+	</li>
+	
+</ol>
+
+<!-- p>
+	You may want to use [id].getCode() to get the content code from CodePress window and save it to your server since CodePress only edit files and do not save them.
+</p>
+<p>
+	You may also want to open files from server. You'll have to write a server side script and replace the JavaScript call on codepress.js from codepress.html to codepress.php (if your server side language is PHP, of course).
+</p -->
+
+<h3>License</h3>
+<p>
+	CodePress is distributed under the <a href="http://www.opensource.org/licenses/lgpl-license.php">LGPL</a>. If your software is <a href="http://www.gnu.org/philosophy/license-list.html#GPLCompatibleLicenses">compatible</a> with this licence or it is under <a href="http://creativecommons.org/">Creative Commons</a>, you can use it as you want. Just keep the credits somewhere around.
+</p>
+
+
+</div><!--/container-->
+
+
+
+<!-- hidden codes for loading -->
+<textarea id="cp-php" class="hidden-code">
+<?php
+// Very simple implementation of server side script
+
+if(isset($_GET['file'])) {
+	$file = basename($_GET['file']);
+	$full_file = $path['server'].'/'.$path['webdocs'].'/'.$path['files']."/".$file;
+	if(file_exists($full_file)) {
+		$code = file_get_contents($full_file);
+		$code = preg_replace("/>/","&amp;gt;",$code);
+		$code = preg_replace("/</","&amp;lt;",$code);
+		$language = getLanguage($file);
+	}
+}
+?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<title>CodePress - Real Time Syntax Highlighting Editor written in JavaScript</title>
+	<link type="text/css" href="languages/codepress-<?=$language?>.css" rel="stylesheet" id="cp-lang-style" />
+	<script type="text/javascript" src="codepress.js"></script>
+	<script type="text/javascript">
+		CodePress.language = '<?=$language?>';
+	</script>
+</head>
+<body id="ffedt"><pre id="ieedt"><?=$code?></pre></body>
+</html>
+</textarea>
+
+<textarea id="cp-javascript" class="hidden-code">
+CodePress = function(obj) {
+	var self = document.createElement('iframe');
+	self.textarea = obj;
+	self.textarea.disabled = true;
+	self.style.height = self.textarea.clientHeight +'px';
+	self.style.width = self.textarea.clientWidth +'px';
+	
+	self.initialize = function() {
+		self.editor = self.contentWindow.CodePress;
+		self.editor.body = self.contentWindow.document.getElementsByTagName('body')[0];
+		self.editor.setCode(self.textarea.value);
+		self.editor.syntaxHighlight('init');
+	}
+	
+	self.edit = function(id,language) {
+		self.language = (language) ? language : self.textarea.className.replace(/ ?codepress ?/,'');
+		self.src = cpPath+'modules/codepress.html?engine='+self.getEngine()+'&language='+self.language;
+		if(self.attachEvent) self.attachEvent('onload',self.initialize);
+		else self.addEventListener('load',self.initialize,false);
+	}
+}
+</textarea>
+
+
+<textarea id="cp-java" class="hidden-code">
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Project ECCO - File manager class
+ * @author Fernando M.A.d.S.
+ */
+public class FileManager extends HttpServlet {
+
+	private static final long serialVersionUID = 1L;
+	private static String login = "feanndor"; // session var should come here
+	private static String usersPath = System.getProperty("user.dir")+File.separator+"htdocs"+File.separator+"ecco"+File.separator+"users"+File.separator;
+	private static File dir = new File(usersPath+login+File.separator);
+	static boolean existDirectories = false;
+	static int isDirectory = 0;
+
+	public FileFilter filterFiles(File dir) {
+		return (new FileFilter() {
+			public boolean accept(File pathname) {
+				return !(pathname.isDirectory());
+			}
+		});
+	}
+}
+</textarea>
+
+<textarea id="cp-perl" class="hidden-code">
+#!/usr/bin/perl      
+# The first line of the script envokes Perl 
+
+# Scalar variables
+$var1 = "Hello World";   
+$var2 = 14.6;
+
+# Array variables
+ at arr1 = ("zero","one","two","three","four");
+
+# Hash variable, or associative array
+%hash1 = ("one","Monday","two", "Tuesday","three", "Wednesday","four","Thursday");
+
+# Some simple printing
+print $var1; 
+
+# Subroutine
+sub test() {
+	print "ok";
+}
+</textarea>
+
+<textarea id="cp-sql" class="hidden-code">
+--
+-- simple select example
+-- 
+SELECT * FROM books
+	WHERE price > 100.00 and price < 150.00
+	ORDER BY title
+
+SELECT books.title, count(*) AS Authors
+	FROM books
+	JOIN book_authors 
+		ON books.book_number = book_authors.book_number
+	GROUP BY books.title
+
+-- insert, update and delete examples
+	
+INSERT INTO my_table (field1, field2, field3) VALUES ('test', 'N', NULL);
+
+BEGIN WORK;
+	UPDATE inventory SET quantity = quantity - 3 WHERE item = 'pants';
+COMMIT;
+</textarea>
+
+<textarea id="cp-html" class="hidden-code">
+<html>
+<head>
+	<title>CodePress - Online Real Time Syntax Highlighting Editor</title>
+
+	<style type="text/css">
+	@import url(styles.css);	
+	</style>
+	<script type="text/javascript">
+	function getCode() {
+		alert(textWithoutHighlighting);
+	}
+	</script>
+</head>
+<body>
+<div id="logo">
+	<h1><a href="http://codepress.org/">CodePress</a></h1>
+	<h2>Online Real Time Syntax Highlighting Editor</h2>
+	<img src="testimage.gif" />
+</div>
+<div id="languages">
+	<em>choose language:</em> 
+	<button onclick="edit('codepress.php',this)" id="default">PHP</button> 
+	<button onclick="edit('FileManager.java',this)">Java</button> 
+</div>
+</body>
+</html>
+</textarea>
+
+<textarea id="cp-css" class="hidden-code">
+/* CSS comment */
+
+body {
+	color:#000;
+	background-color:white;
+	font:15px Georgia, "Lucida Grande", Arial, sans-serif; 
+	letter-spacing:0.01em;
+	margin:15px;
+}
+
+p { 
+	margin:0 0 15px 0; 
+}
+
+a,a:visited {
+	color:#7f0055;
+}
+
+select {
+	background:#ffffe1;
+}
+
+h1 {
+	color:#7f0055;
+	margin:0;
+	padding:0;
+	font-size:42px;
+}
+</textarea>
+
+
+<script src="codepress.js" type="text/javascript"></script>
+</body>
+</html>

Added: jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,77 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::CodePress;
+use base qw/Jifty::Plugin/;
+
+our $VERSION = '0.02';
+
+=head1 NAME
+
+Jifty::Plugin::CodePress
+
+=head1 DESCRIPTION
+
+CodePress, web-based source code editor with syntax highlighting
+
+=head1 SYNOPSIS
+
+In etc/config.yml
+
+  Plugins:
+    - CodePress: {}
+
+In your View do something like:
+
+  $action->form_field( 'source',
+	cols => 80, rows => 25,
+	language => 'perl',
+	render_as => 'Jifty::Plugin::CodePress::Textarea',
+  );
+
+or if you are using L<Template::Declare>
+
+  render_param(
+  	$action => 'source',
+	cols => 80, rows => 25,
+	language => 'perl',
+	render_as => 'Jifty::Plugin::CodePress::Textarea',
+  );
+
+=head1 VERSION
+
+Created from L<https://codepress.svn.sourceforge.net/svnroot/codepress/trunk/stable>
+revision 219 with bunch of local changes to make it play nicer with Jifty.
+
+This involved some hard-coding of paths (because automatic path detection
+from CodePress doesn't work well with Jifty's expectation of JavaScript code
+in C</js/>), addition of C<CodePress.instances> object to track all
+instances and additional JavaScript event handling using C<DOM.Events>
+to remove requirement to call C<CodePress.beforeSubmit> from form submit
+(If you want you can still call it, and it will turn all CodePress editors
+back to textarea).
+
+This also side-stepped problem with original calling schematic which created
+functions with names from element ids. This was problematic with Jifty
+because ids are automatically generated and use dashes (C<->) in them which
+aren't valid JavaScript function names.
+
+=head1 BUGS
+
+There seems to strange interaction between CodePress and Jifty fragments.
+
+=head1 SEE ALSO
+
+L<http://codepress.org/> - project site
+
+=cut
+
+sub init {
+	my $self = shift;
+	Jifty->web->javascript_libs([
+	@{ Jifty->web->javascript_libs },
+	"codepress.js",
+	]);
+}
+
+1;

Added: jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress/Textarea.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/lib/Jifty/Plugin/CodePress/Textarea.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,41 @@
+use warnings;
+use strict;
+
+package Jifty::Plugin::CodePress::Textarea;
+use base qw(Jifty::Web::Form::Field::Textarea);
+
+__PACKAGE__->mk_accessors(qw(language));
+
+=head2 accessors
+
+Provide C<language> accessor, in addition to
+L<Jifty::Web::Form::Field::Textarea>'s default accessors.
+
+=cut
+
+sub accessors { shift->SUPER::accessors(), 'language' }
+
+=head2 render_widget
+
+Renders the textarea widget.
+
+=cut
+
+sub render_widget {
+    my $self  = shift;
+    my $field;
+    $field .= qq!<textarea!;
+    $field .= qq! name="@{[ $self->input_name ]}"!;
+    $field .= qq! id="@{[ $self->element_id ]}"!;
+    $field .= qq! rows="@{[$self->rows || 25]}"!;
+    $field .= qq! cols="@{[$self->cols || 80]}"!;
+    $field .= $self->_widget_class( 'codepress', $self->language );
+    $field .= qq! >!;
+    $field .= Jifty->web->escape($self->current_value) if $self->current_value;
+    $field .= qq!</textarea>\n!;
+
+    Jifty->web->out($field);
+    '';
+}
+
+1;

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/codepress.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/codepress.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,7 @@
+body {margin-top:13px;_margin-top:14px;background:white;font-family:monospace;font-size:13px;margin-left:32px;white-space:pre;background-image:url("images/line-numbers.png");background-repeat:repeat-y;background-position:0 3px;line-height:16px;}
+html>body{background-position:0 2px;}
+P {margin:0;padding:0;border:0;outline:0;display:block;white-space:pre;}
+b, i, s, u, a, em, tt, ins, big, cite, strong, var, dfn {text-decoration:none;font-weight:normal;font-style:normal;font-size:13px;}
+
+body.hide-line-numbers {background:white;margin-left:16px;}
+body.show-line-numbers {background-image:url("images/line-numbers.png");margin-left:32px;}
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/codepress.html
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/codepress.html	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+	<title>CodePress - Real Time Syntax Highlighting Editor written in JavaScript</title>
+	<meta name="description" content="CodePress - source code editor window" />
+
+	<script type="text/javascript">
+	var language = 'generic';
+	var engine = 'older';
+	var ua = navigator.userAgent;
+	var ts = (new Date).getTime(); // timestamp to avoid cache
+	var lh = location.href;
+	
+	if(ua.match('MSIE')) engine = 'msie';
+	else if(ua.match('KHTML')) engine = 'khtml'; 
+	else if(ua.match('Opera')) engine = 'opera'; 
+	else if(ua.match('Gecko')) engine = 'gecko';
+
+	if(lh.match('language=')) language = lh.replace(/.*language=(.*?)(&.*)?$/,'$1');
+
+	document.write('<link type="text/css" href="/static/codepress/codepress.css?ts='+ts+'" rel="stylesheet" />');
+	document.write('<link type="text/css" href="/static/codepress/languages/'+language+'.css?ts='+ts+'" rel="stylesheet" id="cp-lang-style" />');
+	document.write('<scr'+'ipt type="text/javascript" src="/static/codepress/engines/'+engine+'.js?ts='+ts+'"></scr'+'ipt>');
+	document.write('<scr'+'ipt type="text/javascript" src="/static/codepress/languages/'+language+'.js?ts='+ts+'"></scr'+'ipt>');
+	</script>
+
+</head>
+
+<script type="text/javascript">
+if (engine == "gecko") document.write('<body> </body>');
+else if(engine == "msie") document.write('<body><pre></pre></body>');
+else if(engine == "opera") document.write('<body></body>');
+// else if(engine == "khtml") document.write('<body> </body>');
+</script>
+
+</html>

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/gecko.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/gecko.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,273 @@
+/*
+ * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.org/
+ * 
+ * Copyright (C) 2007 Fernando M.A.d.S. <fermads at gmail.com>
+ *
+ * Developers:
+ *		Fernando M.A.d.S. <fermads at gmail.com>
+ *		Michael Hurni <michael.hurni at gmail.com>
+ * Contributors: 	
+ *		Martin D. Kirk
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the 
+ * GNU Lesser General Public License as published by the Free Software Foundation.
+ * 
+ * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
+ */
+
+CodePress = {
+	scrolling : false,
+	autocomplete : true,
+
+	// set initial vars and start sh
+	initialize : function() {
+		if(typeof(editor)=='undefined' && !arguments[0]) return;
+		chars = '|32|46|62|'; // charcodes that trigger syntax highlighting
+		cc = '\u2009'; // control char
+		editor = document.getElementsByTagName('body')[0];
+		document.designMode = 'on';
+		document.addEventListener('keypress', this.keyHandler, true);
+		window.addEventListener('scroll', function() { if(!CodePress.scrolling) CodePress.syntaxHighlight('scroll') }, false);
+		completeChars = this.getCompleteChars();
+		completeEndingChars =  this.getCompleteEndingChars();
+	},
+
+	// treat key bindings
+	keyHandler : function(evt) {
+    	keyCode = evt.keyCode;	
+		charCode = evt.charCode;
+		fromChar = String.fromCharCode(charCode);
+
+		if((evt.ctrlKey || evt.metaKey) && evt.shiftKey && charCode!=90)  { // shortcuts = ctrl||appleKey+shift+key!=z(undo) 
+			CodePress.shortcuts(charCode?charCode:keyCode);
+		}
+		else if( (completeEndingChars.indexOf('|'+fromChar+'|')!= -1 || completeChars.indexOf('|'+fromChar+'|')!=-1) && CodePress.autocomplete) { // auto complete
+			if(!CodePress.completeEnding(fromChar))
+			     CodePress.complete(fromChar);
+		}
+	    else if(chars.indexOf('|'+charCode+'|')!=-1||keyCode==13) { // syntax highlighting
+			top.setTimeout(function(){CodePress.syntaxHighlight('generic');},100);
+		}
+		else if(keyCode==9 || evt.tabKey) {  // snippets activation (tab)
+			CodePress.snippets(evt);
+		}
+		else if(keyCode==46||keyCode==8) { // save to history when delete or backspace pressed
+		 	CodePress.actions.history[CodePress.actions.next()] = editor.innerHTML;
+		}
+		else if((charCode==122||charCode==121||charCode==90) && evt.ctrlKey) { // undo and redo
+			(charCode==121||evt.shiftKey) ? CodePress.actions.redo() :  CodePress.actions.undo(); 
+			evt.preventDefault();
+		}
+		else if(charCode==118 && evt.ctrlKey)  { // handle paste
+		 	top.setTimeout(function(){CodePress.syntaxHighlight('generic');},100);
+		}
+		else if(charCode==99 && evt.ctrlKey)  { // handle cut
+		 	//alert(window.getSelection().getRangeAt(0).toString().replace(/\t/g,'FFF'));
+		}
+
+	},
+
+	// put cursor back to its original position after every parsing
+	findString : function() {
+		if(self.find(cc))
+			window.getSelection().getRangeAt(0).deleteContents();
+	},
+	
+	// split big files, highlighting parts of it
+	split : function(code,flag) {
+		if(flag=='scroll') {
+			this.scrolling = true;
+			return code;
+		}
+		else {
+			this.scrolling = false;
+			mid = code.indexOf(cc);
+			if(mid-2000<0) {ini=0;end=4000;}
+			else if(mid+2000>code.length) {ini=code.length-4000;end=code.length;}
+			else {ini=mid-2000;end=mid+2000;}
+			code = code.substring(ini,end);
+			return code;
+		}
+	},
+	
+	// syntax highlighting parser
+	syntaxHighlight : function(flag) {
+		//if(document.designMode=='off') document.designMode='on'
+		if(flag!='init') window.getSelection().getRangeAt(0).insertNode(document.createTextNode(cc));
+
+		o = editor.innerHTML;
+		o = o.replace(/<br>/g,'\n');
+		o = o.replace(/<.*?>/g,'');
+		x = z = this.split(o,flag);
+		x = x.replace(/\n/g,'<br>');
+
+		if(arguments[1]&&arguments[2]) x = x.replace(arguments[1],arguments[2]);
+	
+		for(i=0;i<Language.syntax.length;i++) 
+			x = x.replace(Language.syntax[i].input,Language.syntax[i].output);
+
+		editor.innerHTML = this.actions.history[this.actions.next()] = (flag=='scroll') ? x : o.split(z).join(x); 
+		if(flag!='init') this.findString();
+	},
+	
+	getLastWord : function() {
+		var rangeAndCaret = CodePress.getRangeAndCaret();
+		words = rangeAndCaret[0].substring(rangeAndCaret[1]-40,rangeAndCaret[1]);
+		words = words.replace(/[\s\n\r\);\W]/g,'\n').split('\n');
+		return words[words.length-1].replace(/[\W]/gi,'').toLowerCase();
+	},
+	
+	snippets : function(evt) {
+		var snippets = Language.snippets;	
+		var trigger = this.getLastWord();
+		for (var i=0; i<snippets.length; i++) {
+			if(snippets[i].input == trigger) {
+				var content = snippets[i].output.replace(/</g,'&lt;');
+				content = content.replace(/>/g,'&gt;');
+				if(content.indexOf('$0')<0) content += cc;
+				else content = content.replace(/\$0/,cc);
+				content = content.replace(/\n/g,'<br>');
+				var pattern = new RegExp(trigger+cc,'gi');
+				evt.preventDefault(); // prevent the tab key from being added
+				this.syntaxHighlight('snippets',pattern,content);
+			}
+		}
+	},
+	
+	readOnly : function() {
+		document.designMode = (arguments[0]) ? 'off' : 'on';
+	},
+
+	complete : function(trigger) {
+		window.getSelection().getRangeAt(0).deleteContents();
+		var complete = Language.complete;
+		for (var i=0; i<complete.length; i++) {
+			if(complete[i].input == trigger) {
+				var pattern = new RegExp('\\'+trigger+cc);
+				var content = complete[i].output.replace(/\$0/g,cc);
+				parent.setTimeout(function () { CodePress.syntaxHighlight('complete',pattern,content)},0); // wait for char to appear on screen
+			}
+		}
+	},
+
+	getCompleteChars : function() {
+		var cChars = '';
+		for(var i=0;i<Language.complete.length;i++)
+			cChars += '|'+Language.complete[i].input;
+		return cChars+'|';
+	},
+	
+	getCompleteEndingChars : function() {
+		var cChars = '';
+		for(var i=0;i<Language.complete.length;i++)
+			cChars += '|'+Language.complete[i].output.charAt(Language.complete[i].output.length-1);
+		return cChars+'|';
+	},
+	
+	completeEnding : function(trigger) {
+		var range = window.getSelection().getRangeAt(0);
+		try {
+			range.setEnd(range.endContainer, range.endOffset+1)
+		}
+		catch(e) {
+			return false;
+		}
+		var next_character = range.toString()
+		range.setEnd(range.endContainer, range.endOffset-1)
+		if(next_character != trigger) return false;
+		else {
+			range.setEnd(range.endContainer, range.endOffset+1)
+			range.deleteContents();
+			return true;
+		}
+	},
+	
+	shortcuts : function() {
+		var cCode = arguments[0];
+		if(cCode==13) cCode = '[enter]';
+		else if(cCode==32) cCode = '[space]';
+		else cCode = '['+String.fromCharCode(charCode).toLowerCase()+']';
+		for(var i=0;i<Language.shortcuts.length;i++)
+			if(Language.shortcuts[i].input == cCode)
+				this.insertCode(Language.shortcuts[i].output,false);
+	},
+	
+	getRangeAndCaret : function() {	
+		var range = window.getSelection().getRangeAt(0);
+		var range2 = range.cloneRange();
+		var node = range.endContainer;			
+		var caret = range.endOffset;
+		range2.selectNode(node);	
+		return [range2.toString(),caret];
+	},
+	
+	insertCode : function(code,replaceCursorBefore) {
+		var range = window.getSelection().getRangeAt(0);
+		var node = window.document.createTextNode(code);
+		var selct = window.getSelection();
+		var range2 = range.cloneRange();
+		// Insert text at cursor position
+		selct.removeAllRanges();
+		range.deleteContents();
+		range.insertNode(node);
+		// Move the cursor to the end of text
+		range2.selectNode(node);		
+		range2.collapse(replaceCursorBefore);
+		selct.removeAllRanges();
+		selct.addRange(range2);
+	},
+	
+	// get code from editor
+	getCode : function() {
+		var code = editor.innerHTML;
+		code = code.replace(/<br>/g,'\n');
+		code = code.replace(/\u2009/g,'');
+		code = code.replace(/<.*?>/g,'');
+		code = code.replace(/&lt;/g,'<');
+		code = code.replace(/&gt;/g,'>');
+		code = code.replace(/&amp;/gi,'&');
+		return code;
+	},
+
+	// put code inside editor
+	setCode : function() {
+		var code = arguments[0];
+		code = code.replace(/\u2009/gi,'');
+		code = code.replace(/&/gi,'&amp;');
+       	code = code.replace(/</g,'&lt;');
+        code = code.replace(/>/g,'&gt;');
+		editor.innerHTML = code;
+	},
+
+	// undo and redo methods
+	actions : {
+		pos : -1, // actual history position
+		history : [], // history vector
+		
+		undo : function() {
+			if(editor.innerHTML.indexOf(cc)==-1){
+				window.getSelection().getRangeAt(0).insertNode(document.createTextNode(cc));
+			 	this.history[this.pos] = editor.innerHTML;
+			}
+			this.pos--;
+			if(typeof(this.history[this.pos])=='undefined') this.pos++;
+			editor.innerHTML = this.history[this.pos];
+			CodePress.findString();
+		},
+		
+		redo : function() {
+			this.pos++;
+			if(typeof(this.history[this.pos])=='undefined') this.pos--;
+			editor.innerHTML = this.history[this.pos];
+			CodePress.findString();
+		},
+		
+		next : function() { // get next vector position and clean old ones
+			if(this.pos>20) this.history[this.pos-21] = undefined;
+			return ++this.pos;
+		}
+	}
+}
+
+Language={};
+window.addEventListener('load', function() { CodePress.initialize('new'); }, true);
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/khtml.js
==============================================================================

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/msie.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/msie.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,304 @@
+/*
+ * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.org/
+ * 
+ * Copyright (C) 2007 Fernando M.A.d.S. <fermads at gmail.com>
+ *
+ * Developers:
+ *		Fernando M.A.d.S. <fermads at gmail.com>
+ *		Michael Hurni <michael.hurni at gmail.com>
+ * Contributors: 	
+ *		Martin D. Kirk
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the 
+ * GNU Lesser General Public License as published by the Free Software Foundation.
+ * 
+ * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
+ */
+
+CodePress = {
+	scrolling : false,
+	autocomplete : true,
+	
+	// set initial vars and start sh
+	initialize : function() {
+		if(typeof(editor)=='undefined' && !arguments[0]) return;
+		chars = '|32|46|62|'; // charcodes that trigger syntax highlighting
+		cc = '\u2009'; // control char
+		editor = document.getElementsByTagName('pre')[0];
+		editor.contentEditable = 'true';
+		document.getElementsByTagName('body')[0].onfocus = function() {editor.focus();}
+		document.attachEvent('onkeydown', this.metaHandler);
+		document.attachEvent('onkeypress', this.keyHandler);
+		window.attachEvent('onscroll', function() { if(!CodePress.scrolling) setTimeout(function(){CodePress.syntaxHighlight('scroll')},1)});
+		completeChars = this.getCompleteChars();
+		completeEndingChars =  this.getCompleteEndingChars();
+		setTimeout(function() { window.scroll(0,0) },50); // scroll IE to top
+	},
+	
+	// treat key bindings
+	keyHandler : function(evt) {
+		charCode = evt.keyCode;
+		fromChar = String.fromCharCode(charCode);
+		
+		if( (completeEndingChars.indexOf('|'+fromChar+'|')!= -1 || completeChars.indexOf('|'+fromChar+'|')!=-1  )&& CodePress.autocomplete) { // auto complete
+			if(!CodePress.completeEnding(fromChar))
+			     CodePress.complete(fromChar);
+		}
+	    else if(chars.indexOf('|'+charCode+'|')!=-1||charCode==13) { // syntax highlighting
+		 	CodePress.syntaxHighlight('generic');
+		}
+	},
+
+	metaHandler : function(evt) {
+		keyCode = evt.keyCode;
+		
+		if(keyCode==9 || evt.tabKey) { 
+			CodePress.snippets();
+		}
+		else if((keyCode==122||keyCode==121||keyCode==90) && evt.ctrlKey) { // undo and redo
+			(keyCode==121||evt.shiftKey) ? CodePress.actions.redo() :  CodePress.actions.undo(); 
+			evt.returnValue = false;
+		}
+		else if(keyCode==34||keyCode==33) { // handle page up/down for IE
+			self.scrollBy(0, (keyCode==34) ? 200 : -200); 
+			evt.returnValue = false;
+		}
+		else if(keyCode==46||keyCode==8) { // save to history when delete or backspace pressed
+		 	CodePress.actions.history[CodePress.actions.next()] = editor.innerHTML;
+		}
+		else if((evt.ctrlKey || evt.metaKey) && evt.shiftKey && keyCode!=90)  { // shortcuts = ctrl||appleKey+shift+key!=z(undo) 
+			CodePress.shortcuts(keyCode);
+			evt.returnValue = false;
+		}
+		else if(keyCode==86 && evt.ctrlKey)  { // handle paste
+			window.clipboardData.setData('Text',window.clipboardData.getData('Text').replace(/\t/g,'\u2008'));
+		 	top.setTimeout(function(){CodePress.syntaxHighlight('paste');},10);
+		}
+		else if(keyCode==67 && evt.ctrlKey)  { // handle cut
+			// window.clipboardData.setData('Text',x[0]);
+			// code = window.clipboardData.getData('Text');
+		}
+	},
+
+	// put cursor back to its original position after every parsing
+	
+	
+	findString : function() {
+		range = self.document.body.createTextRange();
+		if(range.findText(cc)){
+			range.select();
+			range.text = '';
+		}
+	},
+	
+	// split big files, highlighting parts of it
+	split : function(code,flag) {
+		if(flag=='scroll') {
+			this.scrolling = true;
+			return code;
+		}
+		else {
+			this.scrolling = false;
+			mid = code.indexOf(cc);
+			if(mid-2000<0) {ini=0;end=4000;}
+			else if(mid+2000>code.length) {ini=code.length-4000;end=code.length;}
+			else {ini=mid-2000;end=mid+2000;}
+			code = code.substring(ini,end);
+			return code.substring(code.indexOf('<P>'),code.lastIndexOf('</P>')+4);
+		}
+	},
+	
+	// syntax highlighting parser
+	syntaxHighlight : function(flag) {
+		if(flag!='init') document.selection.createRange().text = cc;
+		o = editor.innerHTML;
+		if(flag=='paste') { // fix pasted text
+			o = o.replace(/<BR>/g,'\r\n'); 
+			o = o.replace(/\u2008/g,'\t');
+		}
+		o = o.replace(/<P>/g,'\n');
+		o = o.replace(/<\/P>/g,'\r');
+		o = o.replace(/<.*?>/g,'');
+		o = o.replace(/&nbsp;/g,'');			
+		o = '<PRE><P>'+o+'</P></PRE>';
+		o = o.replace(/\n\r/g,'<P></P>');
+		o = o.replace(/\n/g,'<P>');
+		o = o.replace(/\r/g,'<\/P>');
+		o = o.replace(/<P>(<P>)+/,'<P>');
+		o = o.replace(/<\/P>(<\/P>)+/,'</P>');
+		o = o.replace(/<P><\/P>/g,'<P><BR/></P>');
+		x = z = this.split(o,flag);
+
+		if(arguments[1]&&arguments[2]) x = x.replace(arguments[1],arguments[2]);
+	
+		for(i=0;i<Language.syntax.length;i++) 
+			x = x.replace(Language.syntax[i].input,Language.syntax[i].output);
+			
+		editor.innerHTML = this.actions.history[this.actions.next()] = (flag=='scroll') ? x : o.replace(z,x);
+		if(flag!='init') this.findString();
+	},
+
+	snippets : function(evt) {
+		var snippets = Language.snippets;
+		var trigger = this.getLastWord();
+		for (var i=0; i<snippets.length; i++) {
+			if(snippets[i].input == trigger) {
+				var content = snippets[i].output.replace(/</g,'&lt;');
+				content = content.replace(/>/g,'&gt;');
+				if(content.indexOf('$0')<0) content += cc;
+				else content = content.replace(/\$0/,cc);
+				content = content.replace(/\n/g,'</P><P>');
+				var pattern = new RegExp(trigger+cc,"gi");
+				this.syntaxHighlight('snippets',pattern,content);
+			}
+		}
+	},
+	
+	readOnly : function() {
+		editor.contentEditable = (arguments[0]) ? 'false' : 'true';
+	},
+	
+	complete : function(trigger) {
+		var complete = Language.complete;
+		for (var i=0; i<complete.length; i++) {
+			if(complete[i].input == trigger) {
+				var pattern = new RegExp('\\'+trigger+cc);
+				var content = complete[i].output.replace(/\$0/g,cc);
+				setTimeout(function () { CodePress.syntaxHighlight('complete',pattern,content)},0); // wait for char to appear on screen
+			}
+		}
+	},
+	
+	getCompleteChars : function() {
+		var cChars = '';
+		for(var i=0;i<Language.complete.length;i++)
+			cChars += '|'+Language.complete[i].input;
+		return cChars+'|';
+	},
+
+	getCompleteEndingChars : function() {
+		var cChars = '';
+		for(var i=0;i<Language.complete.length;i++)
+			cChars += '|'+Language.complete[i].output.charAt(Language.complete[i].output.length-1);
+		return cChars+'|';
+	},
+
+	completeEnding : function(trigger) {
+		var range = document.selection.createRange();
+		try {
+			range.moveEnd('character', 1)
+		}
+		catch(e) {
+			return false;
+		}
+		var next_character = range.text
+		range.moveEnd('character', -1)
+		if(next_character != trigger )  return false;
+		else {
+			range.moveEnd('character', 1)
+			range.text=''
+			return true;
+		}
+	},	
+
+	shortcuts : function() {
+		var cCode = arguments[0];
+		if(cCode==13) cCode = '[enter]';
+		else if(cCode==32) cCode = '[space]';
+		else cCode = '['+String.fromCharCode(keyCode).toLowerCase()+']';
+		for(var i=0;i<Language.shortcuts.length;i++)
+			if(Language.shortcuts[i].input == cCode)
+				this.insertCode(Language.shortcuts[i].output,false);
+	},
+	
+	getLastWord : function() {
+		var rangeAndCaret = CodePress.getRangeAndCaret();
+		words = rangeAndCaret[0].substring(rangeAndCaret[1]-40,rangeAndCaret[1]);
+		words = words.replace(/[\s\n\r\);\W]/g,'\n').split('\n');
+		return words[words.length-1].replace(/[\W]/gi,'').toLowerCase();
+	}, 
+
+	getRangeAndCaret : function() {	
+		var range = document.selection.createRange();
+		var caret = Math.abs(range.moveStart('character', -1000000)+1);
+		range = this.getCode();
+		range = range.replace(/\n\r/gi,'  ');
+		range = range.replace(/\n/gi,'');
+		return [range.toString(),caret];
+	},
+	
+	insertCode : function(code,replaceCursorBefore) {
+		var repdeb = '';
+		var repfin = '';
+		
+		if(replaceCursorBefore) { repfin = code; }
+		else { repdeb = code; }
+		
+		if(typeof document.selection != 'undefined') {
+			var range = document.selection.createRange();
+			range.text = repdeb + repfin;
+			range = document.selection.createRange();
+			range.move('character', -repfin.length);
+			range.select();	
+		}	
+	},
+
+	// get code from editor	
+	getCode : function() {
+		var code = editor.innerHTML;
+		code = code.replace(/<br>/g,'\n');
+		code = code.replace(/<\/p>/gi,'\r');
+		code = code.replace(/<p>/i,''); // IE first line fix
+		code = code.replace(/<p>/gi,'\n');
+		code = code.replace(/&nbsp;/gi,'');
+		code = code.replace(/\u2009/g,'');
+		code = code.replace(/<.*?>/g,'');
+		code = code.replace(/&lt;/g,'<');
+		code = code.replace(/&gt;/g,'>');
+		code = code.replace(/&amp;/gi,'&');
+		return code;
+	},
+
+	// put code inside editor
+	setCode : function() {
+		var code = arguments[0];
+		code = code.replace(/\u2009/gi,'');
+		code = code.replace(/&/gi,'&amp;');		
+       	code = code.replace(/</g,'&lt;');
+        code = code.replace(/>/g,'&gt;');
+		editor.innerHTML = '<pre>'+code+'</pre>';
+	},
+
+	
+	// undo and redo methods
+	actions : {
+		pos : -1, // actual history position
+		history : [], // history vector
+		
+		undo : function() {
+			if(editor.innerHTML.indexOf(cc)==-1){
+				document.selection.createRange().text = cc;
+			 	this.history[this.pos] = editor.innerHTML;
+			}
+			this.pos--;
+			if(typeof(this.history[this.pos])=='undefined') this.pos++;
+			editor.innerHTML = this.history[this.pos];
+			CodePress.findString();
+		},
+		
+		redo : function() {
+			this.pos++;
+			if(typeof(this.history[this.pos])=='undefined') this.pos--;
+			editor.innerHTML = this.history[this.pos];
+			CodePress.findString();
+		},
+		
+		next : function() { // get next vector position and clean old ones
+			if(this.pos>20) this.history[this.pos-21] = undefined;
+			return ++this.pos;
+		}
+	}
+}
+
+Language={};
+window.attachEvent('onload', function() { CodePress.initialize('new');});
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/older.js
==============================================================================

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/opera.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/engines/opera.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,260 @@
+/*
+ * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.org/
+ * 
+ * Copyright (C) 2007 Fernando M.A.d.S. <fermads at gmail.com>
+ *
+ * Contributors :
+ *
+ * 	Michael Hurni <michael.hurni at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the 
+ * GNU Lesser General Public License as published by the Free Software Foundation.
+ * 
+ * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
+ */
+
+
+CodePress = {
+	scrolling : false,
+	autocomplete : true,
+
+	// set initial vars and start sh
+	initialize : function() {
+		if(typeof(editor)=='undefined' && !arguments[0]) return;
+		chars = '|32|46|62|'; // charcodes that trigger syntax highlighting
+		cc = '\u2009'; // control char
+		editor = document.getElementsByTagName('body')[0];
+		document.designMode = 'on';
+		document.addEventListener('keyup', this.keyHandler, true);
+		window.addEventListener('scroll', function() { if(!CodePress.scrolling) CodePress.syntaxHighlight('scroll') }, false);
+		completeChars = this.getCompleteChars();
+//		CodePress.syntaxHighlight('init');
+	},
+
+	// treat key bindings
+	keyHandler : function(evt) {
+    	keyCode = evt.keyCode;	
+		charCode = evt.charCode;
+
+		if((evt.ctrlKey || evt.metaKey) && evt.shiftKey && charCode!=90)  { // shortcuts = ctrl||appleKey+shift+key!=z(undo) 
+			CodePress.shortcuts(charCode?charCode:keyCode);
+		}
+		else if(completeChars.indexOf('|'+String.fromCharCode(charCode)+'|')!=-1 && CodePress.autocomplete) { // auto complete
+			CodePress.complete(String.fromCharCode(charCode));
+		}
+	    else if(chars.indexOf('|'+charCode+'|')!=-1||keyCode==13) { // syntax highlighting
+		 	CodePress.syntaxHighlight('generic');
+		}
+		else if(keyCode==9 || evt.tabKey) {  // snippets activation (tab)
+			CodePress.snippets(evt);
+		}
+		else if(keyCode==46||keyCode==8) { // save to history when delete or backspace pressed
+		 	CodePress.actions.history[CodePress.actions.next()] = editor.innerHTML;
+		}
+		else if((charCode==122||charCode==121||charCode==90) && evt.ctrlKey) { // undo and redo
+			(charCode==121||evt.shiftKey) ? CodePress.actions.redo() :  CodePress.actions.undo(); 
+			evt.preventDefault();
+		}
+		else if(keyCode==86 && evt.ctrlKey)  { // paste
+			// TODO: pasted text should be parsed and highlighted
+		}
+	},
+
+	// put cursor back to its original position after every parsing
+	findString : function() {
+		var sel = window.getSelection();
+		var range = window.document.createRange();
+		var span = window.document.getElementsByTagName('span')[0];
+			
+		range.selectNode(span);
+		sel.removeAllRanges();
+		sel.addRange(range);
+		span.parentNode.removeChild(span);
+		//if(self.find(cc))
+		//window.getSelection().getRangeAt(0).deleteContents();
+	},
+	
+	// split big files, highlighting parts of it
+	split : function(code,flag) {
+		if(flag=='scroll') {
+			this.scrolling = true;
+			return code;
+		}
+		else {
+			this.scrolling = false;
+			mid = code.indexOf('<SPAN>');
+			if(mid-2000<0) {ini=0;end=4000;}
+			else if(mid+2000>code.length) {ini=code.length-4000;end=code.length;}
+			else {ini=mid-2000;end=mid+2000;}
+			code = code.substring(ini,end);
+			return code;
+		}
+	},
+	
+	// syntax highlighting parser
+	syntaxHighlight : function(flag) {
+		//if(document.designMode=='off') document.designMode='on'
+		if(flag!='init') {
+			var span = document.createElement('span');
+			window.getSelection().getRangeAt(0).insertNode(span);
+		}
+
+		o = editor.innerHTML;
+//		o = o.replace(/<br>/g,'\r\n');
+//		o = o.replace(/<(b|i|s|u|a|em|tt|ins|big|cite|strong)?>/g,'');
+		//alert(o)
+		o = o.replace(/<(?!span|\/span|br).*?>/gi,'');
+//		alert(o)
+//		x = o;
+		x = z = this.split(o,flag);
+		//alert(z)
+//		x = x.replace(/\r\n/g,'<br>');
+		x = x.replace(/\t/g, '        ');
+
+
+		if(arguments[1]&&arguments[2]) x = x.replace(arguments[1],arguments[2]);
+	
+		for(i=0;i<Language.syntax.length;i++) 
+			x = x.replace(Language.syntax[i].input,Language.syntax[i].output);
+
+		editor.innerHTML = this.actions.history[this.actions.next()] = (flag=='scroll') ? x : o.split(z).join(x); 
+
+		if(flag!='init') this.findString();
+	},
+	
+	getLastWord : function() {
+		var rangeAndCaret = CodePress.getRangeAndCaret();
+		words = rangeAndCaret[0].substring(rangeAndCaret[1]-40,rangeAndCaret[1]);
+		words = words.replace(/[\s\n\r\);\W]/g,'\n').split('\n');
+		return words[words.length-1].replace(/[\W]/gi,'').toLowerCase();
+	}, 
+	
+	snippets : function(evt) {
+		var snippets = Language.snippets;	
+		var trigger = this.getLastWord();
+		for (var i=0; i<snippets.length; i++) {
+			if(snippets[i].input == trigger) {
+				var content = snippets[i].output.replace(/</g,'&lt;');
+				content = content.replace(/>/g,'&gt;');
+				if(content.indexOf('$0')<0) content += cc;
+				else content = content.replace(/\$0/,cc);
+				content = content.replace(/\n/g,'<br>');
+				var pattern = new RegExp(trigger+cc,'gi');
+				evt.preventDefault(); // prevent the tab key from being added
+				this.syntaxHighlight('snippets',pattern,content);
+			}
+		}
+	},
+	
+	readOnly : function() {
+		document.designMode = (arguments[0]) ? 'off' : 'on';
+	},
+
+	complete : function(trigger) {
+		window.getSelection().getRangeAt(0).deleteContents();
+		var complete = Language.complete;
+		for (var i=0; i<complete.length; i++) {
+			if(complete[i].input == trigger) {
+				var pattern = new RegExp('\\'+trigger+cc);
+				var content = complete[i].output.replace(/\$0/g,cc);
+				parent.setTimeout(function () { CodePress.syntaxHighlight('complete',pattern,content)},0); // wait for char to appear on screen
+			}
+		}
+	},
+
+	getCompleteChars : function() {
+		var cChars = '';
+		for(var i=0;i<Language.complete.length;i++)
+			cChars += '|'+Language.complete[i].input;
+		return cChars+'|';
+	},
+
+	shortcuts : function() {
+		var cCode = arguments[0];
+		if(cCode==13) cCode = '[enter]';
+		else if(cCode==32) cCode = '[space]';
+		else cCode = '['+String.fromCharCode(charCode).toLowerCase()+']';
+		for(var i=0;i<Language.shortcuts.length;i++)
+			if(Language.shortcuts[i].input == cCode)
+				this.insertCode(Language.shortcuts[i].output,false);
+	},
+	
+	getRangeAndCaret : function() {	
+		var range = window.getSelection().getRangeAt(0);
+		var range2 = range.cloneRange();
+		var node = range.endContainer;			
+		var caret = range.endOffset;
+		range2.selectNode(node);	
+		return [range2.toString(),caret];
+	},
+	
+	insertCode : function(code,replaceCursorBefore) {
+		var range = window.getSelection().getRangeAt(0);
+		var node = window.document.createTextNode(code);
+		var selct = window.getSelection();
+		var range2 = range.cloneRange();
+		// Insert text at cursor position
+		selct.removeAllRanges();
+		range.deleteContents();
+		range.insertNode(node);
+		// Move the cursor to the end of text
+		range2.selectNode(node);		
+		range2.collapse(replaceCursorBefore);
+		selct.removeAllRanges();
+		selct.addRange(range2);
+	},
+	
+	// get code from editor
+	getCode : function() {
+		var code = editor.innerHTML;
+		code = code.replace(/<br>/g,'\n');
+		code = code.replace(/\u2009/g,'');
+		code = code.replace(/<.*?>/g,'');
+		code = code.replace(/&lt;/g,'<');
+		code = code.replace(/&gt;/g,'>');
+		code = code.replace(/&amp;/gi,'&');
+		return code;
+	},
+
+	// put code inside editor
+	setCode : function() {
+		var code = arguments[0];
+		code = code.replace(/\u2009/gi,'');
+		code = code.replace(/&/gi,'&amp;');
+       	code = code.replace(/</g,'&lt;');
+        code = code.replace(/>/g,'&gt;');
+		editor.innerHTML = code;
+	},
+
+	// undo and redo methods
+	actions : {
+		pos : -1, // actual history position
+		history : [], // history vector
+		
+		undo : function() {
+			if(editor.innerHTML.indexOf(cc)==-1){
+				window.getSelection().getRangeAt(0).insertNode(document.createTextNode(cc));
+			 	this.history[this.pos] = editor.innerHTML;
+			}
+			this.pos--;
+			if(typeof(this.history[this.pos])=='undefined') this.pos++;
+			editor.innerHTML = this.history[this.pos];
+			CodePress.findString();
+		},
+		
+		redo : function() {
+			this.pos++;
+			if(typeof(this.history[this.pos])=='undefined') this.pos--;
+			editor.innerHTML = this.history[this.pos];
+			CodePress.findString();
+		},
+		
+		next : function() { // get next vector position and clean old ones
+			if(this.pos>20) this.history[this.pos-21] = undefined;
+			return ++this.pos;
+		}
+	}
+}
+
+Language={};
+window.addEventListener('load', function() { CodePress.initialize('new'); }, true);

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/images/line-numbers.png
==============================================================================
Binary file. No diff available.

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/asp.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/asp.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,71 @@
+/*
+ * CodePress color styles for ASP-VB syntax highlighting
+ * By Martin D. Kirk
+ */
+/* tags */
+
+b {
+	color:#000080;
+} 
+/* comments */
+big, big b, big em, big ins, big s, strong i, strong i b, strong i s, strong i u, strong i a, strong i a u, strong i s u {
+	color:gray;
+	font-weight:normal;
+}
+/* ASP comments */
+strong dfn, strong dfn a,strong dfn var, strong dfn a u, strong dfn u{
+	color:gray;
+	font-weight:normal;
+}
+ /* attributes */ 
+s, s b, span s u, span s cite, strong span s {
+	color:#5656fa ;
+	font-weight:normal;
+}
+ /* strings */ 
+strong s,strong s b, strong s u, strong s cite {
+	color:#009900;
+	font-weight:normal;
+}
+strong ins{
+	color:#000000;
+	font-weight:bold;
+}
+ /* Syntax */
+strong a, strong a u {
+	color:#0000FF;
+	font-weight:;
+}
+ /* Native Keywords */
+strong u {
+	color:#990099;
+	font-weight:bold;
+}
+/* Numbers */
+strong var{
+	color:#FF0000;
+}
+/* ASP Language */
+span{
+	color:#990000;
+	font-weight:bold;
+}
+strong i,strong a i, strong u i {
+	color:#009999;
+}
+/* style */
+em {
+	color:#800080;
+	font-style:normal;
+}
+ /* script */ 
+ins {
+	color:#800000;
+	font-weight:bold;
+}
+
+/* <?php and ?> */
+cite, s cite {
+	color:red;
+	font-weight:bold;
+}
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/asp.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/asp.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,117 @@
+/*
+ * CodePress regular expressions for ASP-vbscript syntax highlighting
+ */
+
+// ASP VBScript
+Language.syntax = [
+// all tags
+	{ input : /(&lt;[^!%|!%@]*?&gt;)/g, output : '<b>$1</b>' }, 
+// style tags	
+	{ input : /(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g, output : '<em>$1</em><em>$2</em><em>$3</em>' }, 
+// script tags	
+	{ input : /(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g, output : '<ins>$1</ins><ins>$2</ins><ins>$3</ins>' }, 
+// strings "" and attributes
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, 
+// ASP Comment
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<dfn>\'$1$2</dfn>'}, 
+// <%.*
+	{ input : /(&lt;%)/g, output : '<strong>$1' }, 
+// .*%>	
+	{ input : /(%&gt;)/g, output : '$1</strong>' }, 
+// <%@...%>	
+	{ input : /(&lt;%@)(.+?)(%&gt;)/gi, output : '$1<span>$2</span>$3' }, 
+//Numbers	
+	{ input : /\b([\d]+)\b/g, output : '<var>$1</var>' }, 
+// Reserved Words 1 (Blue)
+	{ input : /\b(And|As|ByRef|ByVal|Call|Case|Class|Const|Dim|Do|Each|Else|ElseIf|Empty|End|Eqv|Exit|False|For|Function)\b/gi, output : '<a>$1</a>' }, 
+	{ input : /\b(Get|GoTo|If|Imp|In|Is|Let|Loop|Me|Mod|Enum|New|Next|Not|Nothing|Null|On|Option|Or|Private|Public|ReDim|Rem)\b/gi, output : '<a>$1</a>' }, 
+	{ input : /\b(Resume|Select|Set|Stop|Sub|Then|To|True|Until|Wend|While|With|Xor|Execute|Randomize|Erase|ExecuteGlobal|Explicit|step)\b/gi, output : '<a>$1</a>' }, 
+// Reserved Words 2 (Purple)	
+	{ input : /\b(Abandon|Abs|AbsolutePage|AbsolutePosition|ActiveCommand|ActiveConnection|ActualSize|AddHeader|AddNew|AppendChunk)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(AppendToLog|Application|Array|Asc|Atn|Attributes|BeginTrans|BinaryRead|BinaryWrite|BOF|Bookmark|Boolean|Buffer|Byte)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(CacheControl|CacheSize|Cancel|CancelBatch|CancelUpdate|CBool|CByte|CCur|CDate|CDbl|Charset|Chr|CInt|Clear)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(ClientCertificate|CLng|Clone|Close|CodePage|CommandText|CommandType|CommandTimeout|CommitTrans|CompareBookmarks|ConnectionString|ConnectionTimeout)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Contents|ContentType|Cookies|Cos|CreateObject|CreateParameter|CSng|CStr|CursorLocation|CursorType|DataMember|DataSource|Date|DateAdd|DateDiff)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(DatePart|DateSerial|DateValue|Day|DefaultDatabase|DefinedSize|Delete|Description|Double|EditMode|Eof|EOF|err|Error)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Exp|Expires|ExpiresAbsolute|Filter|Find|Fix|Flush|Form|FormatCurrency|FormatDateTime|FormatNumber|FormatPercent)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(GetChunk|GetLastError|GetRows|GetString|Global|HelpContext|HelpFile|Hex|Hour|HTMLEncode|IgnoreCase|Index|InStr|InStrRev)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Int|Integer|IsArray|IsClientConnected|IsDate|IsolationLevel|Join|LBound|LCase|LCID|Left|Len|Lock|LockType|Log|Long|LTrim)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(MapPath|MarshalOptions|MaxRecords|Mid|Minute|Mode|Month|MonthName|Move|MoveFirst|MoveLast|MoveNext|MovePrevious|Name|NextRecordset)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Now|Number|NumericScale|ObjectContext|Oct|Open|OpenSchema|OriginalValue|PageCount|PageSize|Pattern|PICS|Precision|Prepared|Property)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Provider|QueryString|RecordCount|Redirect|RegExp|Remove|RemoveAll|Replace|Requery|Request|Response|Resync|Right|Rnd)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(RollbackTrans|RTrim|Save|ScriptTimeout|Second|Seek|Server|ServerVariables|Session|SessionID|SetAbort|SetComplete|Sgn)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Sin|Size|Sort|Source|Space|Split|Sqr|State|StaticObjects|Status|StayInSync|StrComp|String|StrReverse|Supports|Tan|Time)\b/gi, output : '<u>$1</u>' },
+	{ input : /\b(Timeout|Timer|TimeSerial|TimeValue|TotalBytes|Transfer|Trim|Type|Type|UBound|UCase|UnderlyingValue|UnLock|Update|UpdateBatch)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(URLEncode|Value|Value|Version|Weekday|WeekdayName|Write|Year)\b/gi, output : '<u>$1</u>' }, 
+// Reserved Words 3 (Turquis)
+	{ input : /\b(vbBlack|vbRed|vbGreen|vbYellow|vbBlue|vbMagenta|vbCyan|vbWhite|vbBinaryCompare|vbTextCompare)\b/gi, output : '<i>$1</i>' }, 
+  	{ input : /\b(vbSunday|vbMonday|vbTuesday|vbWednesday|vbThursday|vbFriday|vbSaturday|vbUseSystemDayOfWeek)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbFirstJan1|vbFirstFourDays|vbFirstFullWeek|vbGeneralDate|vbLongDate|vbShortDate|vbLongTime|vbShortTime)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbObjectError|vbCr|VbCrLf|vbFormFeed|vbLf|vbNewLine|vbNullChar|vbNullString|vbTab|vbVerticalTab|vbUseDefault|vbTrue)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbFalse|vbEmpty|vbNull|vbInteger|vbLong|vbSingle|vbDouble|vbCurrency|vbDate|vbString|vbObject|vbError|vbBoolean|vbVariant)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbDataObject|vbDecimal|vbByte|vbArray)\b/gi, output : '<i>$1</i>' },
+// html comments
+	{ input : /(&lt;!--.*?--&gt.)/g, output : '<big>$1</big>' } 
+]
+
+Language.Functions = [ 
+  	// Output at index 0, must be the desired tagname surrounding a $1
+	// Name is the index from the regex that marks the functionname
+	{input : /(function|sub)([ ]*?)(\w+)([ ]*?\()/gi , output : '<ins>$1</ins>', name : '$3'}
+]
+
+Language.snippets = [
+//Conditional
+	{ input : 'if', output : 'If $0 Then\n\t\nEnd If' },
+	{ input : 'ifelse', output : 'If $0 Then\n\t\n\nElse\n\t\nEnd If' },
+	{ input : 'case', output : 'Select Case $0\n\tCase ?\n\tCase Else\nEnd Select'},
+//Response
+	{ input : 'rw', output : 'Response.Write( $0 )' },
+	{ input : 'resc', output : 'Response.Cookies( $0 )' },
+	{ input : 'resb', output : 'Response.Buffer'},
+	{ input : 'resflu', output : 'Response.Flush()'},
+	{ input : 'resend', output : 'Response.End'},
+//Request
+	{ input : 'reqc', output : 'Request.Cookies( $0 )' },
+	{ input : 'rq', output : 'Request.Querystring("$0")' },
+	{ input : 'rf', output : 'Request.Form("$0")' },
+//FSO
+	{ input : 'fso', output : 'Set fso = Server.CreateObject("Scripting.FileSystemObject")\n$0' },
+	{ input : 'setfo', output : 'Set fo = fso.getFolder($0)' },
+	{ input : 'setfi', output : 'Set fi = fso.getFile($0)' },
+	{ input : 'twr', output : 'Set f = fso.CreateTextFile($0,true)\'overwrite\nf.WriteLine()\nf.Close'},
+	{ input : 'tre', output : 'Set f = fso.OpenTextFile($0, 1)\nf.ReadAll\nf.Close'},
+//Server
+	{ input : 'mapp', output : 'Server.Mappath($0)' },
+//Loops
+	{ input : 'foreach', output : 'For Each $0 in ?\n\t\nNext' },
+	{ input : 'for', output : 'For $0 to ? step ?\n\t\nNext' },
+	{ input : 'do', output : 'Do While($0)\n\t\nLoop' },
+	{ input : 'untilrs', output : 'do until rs.eof\n\t\nrs.movenext\nloop' },
+//ADO
+	{ input : 'adorec', output : 'Set rs = Server.CreateObject("ADODB.Recordset")' },
+	{ input : 'adocon', output : 'Set Conn = Server.CreateObject("ADODB.Connection")' },
+	{ input : 'adostr', output : 'Set oStr = Server.CreateObject("ADODB.Stream")' },
+//Http Request
+	{ input : 'xmlhttp', output : 'Set xmlHttp = Server.CreateObject("Microsoft.XMLHTTP")\nxmlHttp.open("GET", $0, false)\nxmlHttp.send()\n?=xmlHttp.responseText' },
+	{ input : 'xmldoc', output : 'Set xmldoc = Server.CreateObject("Microsoft.XMLDOM")\nxmldoc.async=false\nxmldoc.load(request)'},
+//Functions
+	{ input : 'func', output : 'Function $0()\n\t\n\nEnd Function'},
+	{ input : 'sub', output : 'Sub $0()\n\t\nEnd Sub'}
+
+]
+
+Language.complete = [
+	//{ input : '\'', output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = [
+	{ input : '[space]', output : '&nbsp;' },
+	{ input : '[enter]', output : '<br />' } ,
+	{ input : '[j]', output : 'testing' },
+	{ input : '[7]', output : '&amp;' }
+]
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/csharp.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/csharp.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,9 @@
+/*
+ * CodePress color styles for Java syntax highlighting
+ * By Edwin de Jonge
+ */
+
+b {color:#7F0055;font-weight:bold;font-style:normal;} /* reserved words */
+a {color:#2A0088;font-weight:bold;font-style:normal;} /* types */
+i, i b, i s {color:#3F7F5F;font-weight:bold;} /* comments */
+s, s b {color:#2A00FF;font-weight:normal;} /* strings */
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/csharp.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/csharp.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,25 @@
+/*
+ * CodePress regular expressions for C# syntax highlighting
+ * By Edwin de Jonge
+ */
+ 
+Language.syntax = [ // C#
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, // strings double quote
+	{ input : /\'(.?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>' }, // strings single quote 
+	{ input : /\b(abstract|as|base|break|case|catch|checked|continue|default|delegate|do|else|event|explicit|extern|false|finally|fixed|for|foreach|get|goto|if|implicit|in|interface|internal|is|lock|namespace|new|null|object|operator|out|override|params|partial|private|protected|public|readonly|ref|return|set|sealed|sizeof|static|stackalloc|switch|this|throw|true|try|typeof|unchecked|unsafe|using|value|virtual|while)\b/g, output : '<b>$1</b>' }, // reserved words
+	{ input : /\b(bool|byte|char|class|double|float|int|interface|long|string|struct|void)\b/g, output : '<a>$1</a>' }, // types
+	{ input : /([^:]|^)\/\/(.*?)(<br|<\/P)/g, output : '$1<i>//$2</i>$3' }, // comments //	
+	{ input : /\/\*(.*?)\*\//g, output : '<i>/*$1*/</i>' } // comments /* */
+];
+
+Language.snippets = [];
+
+Language.complete = [ // Auto complete only for 1 character
+	{input : '\'',output : '\'$0\'' },
+	{input : '"', output : '"$0"' },
+	{input : '(', output : '\($0\)' },
+	{input : '[', output : '\[$0\]' },
+	{input : '{', output : '{\n\t$0\n}' }		
+];
+
+Language.shortcuts = [];
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/css.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/css.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,10 @@
+/*
+ * CodePress color styles for CSS syntax highlighting
+ */
+
+b, b a, b u {color:#000080;} /* tags, ids, classes */
+i, i b, i s, i a, i u {color:gray;} /* comments */
+s, s b {color:#a0a0dd;} /* parameters */
+a {color:#0000ff;} /* keys */
+u {color:red;} /* values */
+

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/css.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/css.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,23 @@
+/*
+ * CodePress regular expressions for CSS syntax highlighting
+ */
+
+// CSS
+Language.syntax = [
+	{ input : /(.*?){(.*?)}/g,output : '<b>$1</b>{<u>$2</u>}' }, // tags, ids, classes, values
+	{ input : /([\w-]*?):([^\/])/g,output : '<a>$1</a>:$2' }, // keys
+	{ input : /\((.*?)\)/g,output : '(<s>$1</s>)' }, // parameters
+	{ input : /\/\*(.*?)\*\//g,output : '<i>/*$1*/</i>'} // comments
+]
+
+Language.snippets = []
+
+Language.complete = [
+	{ input : '\'',output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/generic.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/generic.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,9 @@
+/*
+ * CodePress color styles for generic syntax highlighting
+ */
+
+b {color:#7F0055;font-weight:bold;} /* reserved words */
+u {color:darkblue;font-weight:bold;} /* special words */
+i, i b, i s, i u, i em {color:green;font-weight:normal;} /* comments */
+s, s b, s em {color:#2A00FF;font-weight:normal;} /* strings */
+em {font-weight:bold;} /* special chars */
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/generic.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/generic.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,25 @@
+/*
+ * CodePress regular expressions for generic syntax highlighting
+ */
+ 
+// generic languages
+Language.syntax = [
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, // strings double quote
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>' }, // strings single quote
+	{ input : /\b(abstract|continue|for|new|switch|default|goto|boolean|do|if|private|this|break|double|protected|throw|byte|else|import|public|throws|case|return|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|const|float|while|function|label)\b/g, output : '<b>$1</b>' }, // reserved words
+	{ input : /([\(\){}])/g, output : '<em>$1</em>' }, // special chars;
+	{ input : /([^:]|^)\/\/(.*?)(<br|<\/P)/g, output : '$1<i>//$2</i>$3' }, // comments //
+	{ input : /\/\*(.*?)\*\//g, output : '<i>/*$1*/</i>' } // comments /* */
+]
+
+Language.snippets = []
+
+Language.complete = [
+	{ input : '\'', output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/html.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/html.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,13 @@
+/*
+ * CodePress color styles for HTML syntax highlighting
+ */
+
+b {color:#000080;} /* tags */
+ins, ins b, ins s, ins em {color:gray;} /* comments */
+s, s b {color:#7777e4;} /* attribute values */
+a {color:green;} /* links */
+u {color:#E67300;} /* forms */
+big {color:#db0000;} /* images */
+em, em b {color:#800080;} /* style */
+strong {color:#800000;} /* script */
+tt i {color:darkblue;font-weight:bold;} /* script reserved words */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/html.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/html.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,59 @@
+/*
+ * CodePress regular expressions for HTML syntax highlighting
+ */
+
+// HTML
+Language.syntax = [
+	{ input : /(&lt;[^!]*?&gt;)/g, output : '<b>$1</b>'	}, // all tags
+	{ input : /(&lt;a .*?&gt;|&lt;\/a&gt;)/g, output : '<a>$1</a>' }, // links
+	{ input : /(&lt;img .*?&gt;)/g, output : '<big>$1</big>' }, // images
+	{ input : /(&lt;\/?(button|textarea|form|input|select|option|label).*?&gt;)/g, output : '<u>$1</u>' }, // forms
+	{ input : /(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g, output : '<em>$1</em><em>$2</em><em>$3</em>' }, // style tags
+	{ input : /(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g, output : '<strong>$1</strong><tt>$2</tt><strong>$3</strong>' }, // script tags
+	{ input : /=(".*?")/g, output : '=<s>$1</s>' }, // atributes double quote
+	{ input : /=('.*?')/g, output : '=<s>$1</s>' }, // atributes single quote
+	{ input : /(&lt;!--.*?--&gt.)/g, output : '<ins>$1</ins>' }, // comments 
+	{ input : /\b(alert|window|document|break|continue|do|for|new|this|void|case|default|else|function|return|typeof|while|if|label|switch|var|with|catch|boolean|int|try|false|throws|null|true|goto)\b/g, output : '<i>$1</i>' } // script reserved words 
+]
+
+Language.snippets = [
+	{ input : 'aref', output : '<a href="$0"></a>' },
+	{ input : 'h1', output : '<h1>$0</h1>' },
+	{ input : 'h2', output : '<h2>$0</h2>' },
+	{ input : 'h3', output : '<h3>$0</h3>' },
+	{ input : 'h4', output : '<h4>$0</h4>' },
+	{ input : 'h5', output : '<h5>$0</h5>' },
+	{ input : 'h6', output : '<h6>$0</h6>' },
+	{ input : 'html', output : '<html>\n\t$0\n</html>' },
+	{ input : 'head', output : '<head>\n\t<meta http-equiv="content-type" content="text/html; charset=utf-8" />\n\t<title>$0</title>\n\t\n</head>' },
+	{ input : 'img', output : '<img src="$0" alt="" />' },
+	{ input : 'input', output : '<input name="$0" id="" type="" value="" />' },
+	{ input : 'label', output : '<label for="$0"></label>' },
+	{ input : 'legend', output : '<legend>\n\t$0\n</legend>' },
+	{ input : 'link', output : '<link rel="stylesheet" href="$0" type="text/css" media="screen" charset="utf-8" />' },		
+	{ input : 'base', output : '<base href="$0" />' }, 
+	{ input : 'body', output : '<body>\n\t$0\n</body>' }, 
+	{ input : 'css', output : '<link rel="stylesheet" href="$0" type="text/css" media="screen" charset="utf-8" />' },
+	{ input : 'div', output : '<div>\n\t$0\n</div>' },
+	{ input : 'divid', output : '<div id="$0">\n\t\n</div>' },
+	{ input : 'dl', output : '<dl>\n\t<dt>\n\t\t$0\n\t</dt>\n\t<dd></dd>\n</dl>' },
+	{ input : 'fieldset', output : '<fieldset>\n\t$0\n</fieldset>' },
+	{ input : 'form', output : '<form action="$0" method="" name="">\n\t\n</form>' },
+	{ input : 'meta', output : '<meta name="$0" content="" />' },
+	{ input : 'p', output : '<p>$0</p>' },
+	{ input : 'script', output : '<script type="text/javascript" language="javascript" charset="utf-8">\n\t$0\t\n</script>' },
+	{ input : 'scriptsrc', output : '<script src="$0" type="text/javascript" language="javascript" charset="utf-8"></script>' },
+	{ input : 'span', output : '<span>$0</span>' },
+	{ input : 'table', output : '<table border="$0" cellspacing="" cellpadding="">\n\t<tr><th></th></tr>\n\t<tr><td></td></tr>\n</table>' },
+	{ input : 'style', output : '<style type="text/css" media="screen">\n\t$0\n</style>' }
+]
+	
+Language.complete = [
+	{ input : '\'',output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/java.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/java.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,7 @@
+/*
+ * CodePress color styles for Java syntax highlighting
+ */
+
+b {color:#7F0055;font-weight:bold;font-style:normal;} /* reserved words */
+i, i b, i s {color:#3F7F5F;font-weight:bold;} /* comments */
+s, s b {color:#2A00FF;font-weight:normal;} /* strings */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/java.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/java.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,24 @@
+/*
+ * CodePress regular expressions for Java syntax highlighting
+ */
+ 
+// Java
+Language.syntax = [
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>'}, // strings double quote
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>'}, // strings single quote
+	{ input : /\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/g, output : '<b>$1</b>'}, // reserved words
+	{ input : /([^:]|^)\/\/(.*?)(<br|<\/P)/g, output : '$1<i>//$2</i>$3'}, // comments //	
+	{ input : /\/\*(.*?)\*\//g, output : '<i>/*$1*/</i>' }// comments /* */
+]
+
+Language.snippets = []
+
+Language.complete = [
+	{ input : '\'',output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/javascript.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/javascript.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,8 @@
+/*
+ * CodePress color styles for JavaScript syntax highlighting
+ */
+
+b {color:#7F0055;font-weight:bold;} /* reserved words */
+u {color:darkblue;font-weight:bold;} /* special words */
+i, i b, i s, i u {color:green;font-weight:normal;} /* comments */
+s, s b, s u {color:#2A00FF;font-weight:normal;} /* strings */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/javascript.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/javascript.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,30 @@
+/*
+ * CodePress regular expressions for JavaScript syntax highlighting
+ */
+ 
+// JavaScript
+Language.syntax = [ 
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, // strings double quote
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>' }, // strings single quote
+	{ input : /\b(break|continue|do|for|new|this|void|case|default|else|function|return|typeof|while|if|label|switch|var|with|catch|boolean|int|try|false|throws|null|true|goto)\b/g, output : '<b>$1</b>' }, // reserved words
+	{ input : /\b(alert|isNaN|parent|Array|parseFloat|parseInt|blur|clearTimeout|prompt|prototype|close|confirm|length|Date|location|Math|document|element|name|self|elements|setTimeout|navigator|status|String|escape|Number|submit|eval|Object|event|onblur|focus|onerror|onfocus|onclick|top|onload|toString|onunload|unescape|open|valueOf|window|onmouseover)\b/g, output : '<u>$1</u>' }, // special words
+	{ input : /([^:]|^)\/\/(.*?)(<br|<\/P)/g, output : '$1<i>//$2</i>$3' }, // comments //
+	{ input : /\/\*(.*?)\*\//g, output : '<i>/*$1*/</i>' } // comments /* */
+]
+
+Language.snippets = [
+	{ input : 'dw', output : 'document.write(\'$0\');' },
+	{ input : 'getid', output : 'document.getElementById(\'$0\')' },
+	{ input : 'fun', output : 'function $0(){\n\t\n}' },
+	{ input : 'func', output : 'function $0(){\n\t\n}' }
+]
+
+Language.complete = [
+	{ input : '\'',output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/perl.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/perl.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,11 @@
+/*
+ * CodePress color styles for Perl syntax highlighting
+ * By J. Nick Koston
+ */
+
+b {color:#7F0055;font-weight:bold;} /* reserved words */
+i, i b, i s, i em, i a, i u {color:gray;font-weight:normal;} /* comments */
+s, s b, s a, s em, s u {color:#2A00FF;font-weight:normal;} /* strings */
+a {color:#006700;font-weight:bold;} /* variables */
+em {color:darkblue;font-weight:bold;} /* functions */
+u {font-weight:bold;} /* special chars */
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/perl.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/perl.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,27 @@
+/*
+ * CodePress regular expressions for Perl syntax highlighting
+ * By J. Nick Koston
+ */
+
+// Perl
+Language.syntax = [ 
+	{ input  : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, // strings double quote
+	{ input  : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>' }, // strings single quote
+	{ input  : /([\$\@\%][\w\.]*)/g, output : '<a>$1</a>' }, // vars
+	{ input  : /(sub\s+)([\w\.]*)/g, output : '$1<em>$2</em>' }, // functions
+	{ input  : /\b(abs|accept|alarm|atan2|bind|binmode|bless|caller|chdir|chmod|chomp|chop|chown|chr|chroot|close|closedir|connect|continue|cos|crypt|dbmclose|dbmopen|defined|delete|die|do|dump|each|else|elsif|endgrent|endhostent|endnetent|endprotoent|endpwent|eof|eval|exec|exists|exit|fcntl|fileno|find|flock|for|foreach|fork|format|formlinegetc|getgrent|getgrgid|getgrnam|gethostbyaddr|gethostbyname|gethostent|getlogin|getnetbyaddr|getnetbyname|getnetent|getpeername|getpgrp|getppid|getpriority|getprotobyname|getprotobynumber|getprotoent|getpwent|getpwnam|getpwuid|getservbyaddr|getservbyname|getservbyport|getservent|getsockname|getsockopt|glob|gmtime|goto|grep|hex|hostname|if|import|index|int|ioctl|join|keys|kill|last|lc|lcfirst|length|link|listen|LoadExternals|local|localtime|log|lstat|map|mkdir|msgctl|msgget|msgrcv|msgsnd|my|next|no|oct|open|opendir|ordpack|package|pipe|pop|pos|print|printf|push|pwd|qq|quotemeta|qw|rand|read|readdir|readlink|recv|redo|ref|rename|require|reset|return|reverse|rewinddir|rindex|rmdir|scalar|seek|seekdir|select|semctl|semget|semop|send|setgrent|sethostent|setnetent|setpgrp|setpriority|setprotoent|setpwent|setservent|setsockopt|shift|shmctl|shmget|shmread|shmwrite|shutdown|sin|sleep|socket|socketpair|sort|splice|split|sprintf|sqrt|srand|stat|stty|study|sub|substr|symlink|syscall|sysopen|sysread|system|syswritetell|telldir|tie|tied|time|times|tr|truncate|uc|ucfirst|umask|undef|unless|unlink|until|unpack|unshift|untie|use|utime|values|vec|waitpid|wantarray|warn|while|write)\b/g, output : '<b>$1</b>' }, // reserved words
+	{ input  : /([\(\){}])/g, output : '<u>$1</u>' }, // special chars
+	{ input  : /#(.*?)(<br>|<\/P>)/g, output : '<i>#$1</i>$2' } // comments
+]
+
+Language.snippets = []
+
+Language.complete = [
+	{ input : '\'',output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/php.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/php.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,12 @@
+/*
+ * CodePress color styles for PHP syntax highlighting
+ */
+
+b {color:#000080;} /* tags */
+big, big b, big em, big ins, big s, strong i, strong i b, strong i s, strong i u, strong i a, strong i a u, strong i s u {color:gray;font-weight:normal;} /* comments */
+s, s b, strong s u, strong s cite {color:#5656fa;font-weight:normal;} /* attributes and strings */
+strong a, strong a u {color:#006700;font-weight:bold;} /* variables */
+em {color:#800080;font-style:normal;} /* style */
+ins {color:#800000;} /* script */
+strong u {color:#7F0055;font-weight:bold;} /* reserved words */
+cite, s cite {color:red;font-weight:bold;} /* <?php and ?> */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/php.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/php.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,61 @@
+/*
+ * CodePress regular expressions for PHP syntax highlighting
+ */
+
+// PHP
+Language.syntax = [
+	{ input : /(&lt;[^!\?]*?&gt;)/g, output : '<b>$1</b>' }, // all tags
+	{ input : /(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g, output : '<em>$1</em><em>$2</em><em>$3</em>' }, // style tags
+	{ input : /(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g, output : '<ins>$1</ins><ins>$2</ins><ins>$3</ins>' }, // script tags
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, // strings double quote
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>'}, // strings single quote
+	{ input : /(&lt;\?)/g, output : '<strong>$1' }, // <?.*
+	{ input : /(\?&gt;)/g, output : '$1</strong>' }, // .*?>
+	{ input : /(&lt;\?php|&lt;\?=|&lt;\?|\?&gt;)/g, output : '<cite>$1</cite>' }, // php tags
+	{ input : /(\$[\w\.]*)/g, output : '<a>$1</a>' }, // vars
+	{ input : /\b(false|true|and|or|xor|__FILE__|exception|__LINE__|array|as|break|case|class|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|for|foreach|function|global|if|include|include_once|isset|list|new|print|require|require_once|return|static|switch|unset|use|while|__FUNCTION__|__CLASS__|__METHOD__|final|php_user_filter|interface|implements|extends|public|private|protected|abstract|clone|try|catch|throw|this)\b/g, output : '<u>$1</u>' }, // reserved words
+	{ input : /([^:])\/\/(.*?)(<br|<\/P)/g, output : '$1<i>//$2</i>$3' }, // php comments //
+	{ input : /([^:])#(.*?)(<br|<\/P)/g, output : '$1<i>#$2</i>$3' }, // php comments #
+	{ input : /\/\*(.*?)\*\//g, output : '<i>/*$1*/</i>' }, // php comments /* */
+	{ input : /(&lt;!--.*?--&gt.)/g, output : '<big>$1</big>' } // html comments
+]
+
+Language.snippets = [
+	{ input : 'if', output : 'if($0){\n\t\n}' },
+	{ input : 'ifelse', output : 'if($0){\n\t\n}\nelse{\n\t\n}' },
+	{ input : 'else', output : '}\nelse {\n\t' },
+	{ input : 'elseif', output : '}\nelseif($0) {\n\t' },
+	{ input : 'do', output : 'do{\n\t$0\n}\nwhile();' },
+	{ input : 'inc', output : 'include_once("$0");' },
+	{ input : 'fun', output : 'function $0(){\n\t\n}' },	
+	{ input : 'func', output : 'function $0(){\n\t\n}' },	
+	{ input : 'while', output : 'while($0){\n\t\n}' },
+	{ input : 'for', output : 'for($0,,){\n\t\n}' },
+	{ input : 'fore', output : 'foreach($0 as ){\n\t\n}' },
+	{ input : 'foreach', output : 'foreach($0 as ){\n\t\n}' },
+	{ input : 'echo', output : 'echo \'$0\';' },
+	{ input : 'switch', output : 'switch($0) {\n\tcase "": break;\n\tdefault: ;\n}' },
+	{ input : 'case', output : 'case "$0" : break;' },
+	{ input : 'ret0', output : 'return false;' },
+	{ input : 'retf', output : 'return false;' },
+	{ input : 'ret1', output : 'return true;' },
+	{ input : 'rett', output : 'return true;' },
+	{ input : 'ret', output : 'return $0;' },
+	{ input : 'def', output : 'define(\'$0\',\'\');' },
+	{ input : '<?', output : 'php\n$0\n?>' }
+]
+
+Language.complete = [
+	{ input : '\'', output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = [
+	{ input : '[space]', output : '&nbsp;' },
+	{ input : '[enter]', output : '<br />' } ,
+	{ input : '[j]', output : 'testing' },
+	{ input : '[7]', output : '&amp;' }
+]
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/ruby.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/ruby.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,10 @@
+/*
+ * CodePress color styles for Ruby syntax highlighting
+ */
+
+b {color:#7F0055;font-weight:bold;} /* reserved words */
+i, i b, i s, i em, i a, i u {color:gray;font-weight:normal;} /* comments */
+s, s b, s a, s em, s u {color:#2A00FF;font-weight:normal;} /* strings */
+a {color:#006700;font-weight:bold;} /* variables */
+em {color:darkblue;font-weight:bold;} /* functions */
+u {font-weight:bold;} /* special chars */
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/ruby.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/ruby.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,26 @@
+/*
+ * CodePress regular expressions for Perl syntax highlighting
+ */
+
+// Ruby
+Language.syntax = [
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, // strings double quote 
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<s>\'$1$2</s>' }, // strings single quote
+	{ input : /([\$\@\%]+)([\w\.]*)/g, output : '<a>$1$2</a>' }, // vars
+	{ input : /(def\s+)([\w\.]*)/g, output : '$1<em>$2</em>' }, // functions
+	{ input : /\b(alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|false|for|if|in|module|next|nil|not|or|redo|rescue|retry|return|self|super|then|true|undef|unless|until|when|while|yield)\b/g, output : '<b>$1</b>' }, // reserved words
+	{ input  : /([\(\){}])/g, output : '<u>$1</u>' }, // special chars
+	{ input  : /#(.*?)(<br>|<\/P>)/g, output : '<i>#$1</i>$2' } // comments
+];
+
+Language.snippets = []
+
+Language.complete = [
+	{ input : '\'',output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/sql.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/sql.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,10 @@
+/*
+ * CodePress color styles for SQL syntax highlighting
+ * By Merlin Moncure
+ */
+ 
+b {color:#0000FF;font-style:normal;font-weight:bold;} /* reserved words */
+u {color:#FF0000;font-style:normal;} /* types */
+a {color:#CD6600;font-style:normal;font-weight:bold;} /* commands */
+i, i b, i u, i a, i s  {color:#A9A9A9;font-weight:normal;font-style:italic;} /* comments */
+s, s b, s u, s a, s i {color:#2A00FF;font-weight:normal;} /* strings */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/sql.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/sql.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,30 @@
+/*
+ * CodePress regular expressions for SQL syntax highlighting
+ * By Merlin Moncure
+ */
+ 
+// SQL
+Language.syntax = [
+	{ input : /\'(.*?)(\')/g, output : '<s>\'$1$2</s>' }, // strings single quote
+	{ input : /\b(add|after|aggregate|alias|all|and|as|authorization|between|by|cascade|cache|cache|called|case|check|column|comment|constraint|createdb|createuser|cycle|database|default|deferrable|deferred|diagnostics|distinct|domain|each|else|elseif|elsif|encrypted|except|exception|for|foreign|from|from|full|function|get|group|having|if|immediate|immutable|in|increment|initially|increment|index|inherits|inner|input|intersect|into|invoker|is|join|key|language|left|like|limit|local|loop|match|maxvalue|minvalue|natural|nextval|no|nocreatedb|nocreateuser|not|null|of|offset|oids|on|only|operator|or|order|outer|owner|partial|password|perform|plpgsql|primary|record|references|replace|restrict|return|returns|right|row|rule|schema|security|sequence|session|sql|stable|statistics|table|temp|temporary|then|time|to|transaction|trigger|type|unencrypted|union|unique|user|using|valid|value|values|view|volatile|when|where|with|without|zone)\b/gi, output : '<b>$1</b>' }, // reserved words
+	{ input : /\b(bigint|bigserial|bit|boolean|box|bytea|char|character|cidr|circle|date|decimal|double|float4|float8|inet|int2|int4|int8|integer|interval|line|lseg|macaddr|money|numeric|oid|path|point|polygon|precision|real|refcursor|serial|serial4|serial8|smallint|text|timestamp|varbit|varchar)\b/gi, output : '<u>$1</u>' }, // types
+	{ input : /\b(abort|alter|analyze|begin|checkpoint|close|cluster|comment|commit|copy|create|deallocate|declare|delete|drop|end|execute|explain|fetch|grant|insert|listen|load|lock|move|notify|prepare|reindex|reset|restart|revoke|rollback|select|set|show|start|truncate|unlisten|update)\b/gi, output : '<a>$1</a>' }, // commands
+	{ input : /([^:]|^)\-\-(.*?)(<br|<\/P)/g, output: '$1<i>--$2</i>$3' } // comments //	
+]
+
+Language.snippets = [
+	{ input : 'select', output : 'select $0 from  where ' }
+]
+
+Language.complete = [
+	{ input : '\'', output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = []
+
+
+

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/text.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/text.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,5 @@
+/*
+ * CodePress color styles for Text syntax highlighting
+ */
+
+/* do nothing as expected */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/text.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/text.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,9 @@
+/*
+ * CodePress regular expressions for Text syntax highlighting
+ */
+
+// plain text
+Language.syntax = []
+Language.snippets = []
+Language.complete = []
+Language.shortcuts = []

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/vbscript.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/vbscript.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,71 @@
+/*
+ * CodePress color styles for ASP-VB syntax highlighting 
+ * By Martin D. Kirk
+ */
+
+/* tags */
+b {
+	color:#000080;
+} 
+/* comments */
+big, big b, big em, big ins, big s, strong i, strong i b, strong i s, strong i u, strong i a, strong i a u, strong i s u {
+	color:gray;
+	font-weight:normal;
+}
+/* ASP comments */
+strong dfn, strong dfn a,strong dfn var, strong dfn a u, strong dfn u{
+	color:gray;
+	font-weight:normal;
+}
+ /* attributes */ 
+s, s b, span s u, span s cite, strong span s {
+	color:#5656fa ;
+	font-weight:normal;
+}
+ /* strings */ 
+strong s,strong s b, strong s u, strong s cite {
+	color:#009900;
+	font-weight:normal;
+}
+strong ins{
+	color:#000000;
+	font-weight:bold;
+}
+ /* Syntax */
+strong a, strong a u {
+	color:#0000FF;
+	font-weight:;
+}
+ /* Native Keywords */
+strong u {
+	color:#990099;
+	font-weight:bold;
+}
+/* Numbers */
+strong var{
+	color:#FF0000;
+}
+/* ASP Language */
+span{
+	color:#990000;
+	font-weight:bold;
+}
+strong i,strong a i, strong u i {
+	color:#009999;
+}
+/* style */
+em {
+	color:#800080;
+	font-style:normal;
+}
+ /* script */ 
+ins {
+	color:#800000;
+	font-weight:bold;
+}
+
+/* <?php and ?> */
+cite, s cite {
+	color:red;
+	font-weight:bold;
+}
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/vbscript.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/vbscript.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,117 @@
+/*
+ * CodePress regular expressions for ASP-vbscript syntax highlighting
+ */
+
+// ASP VBScript
+Language.syntax = [
+// all tags
+	{ input : /(&lt;[^!%|!%@]*?&gt;)/g, output : '<b>$1</b>' }, 
+// style tags	
+	{ input : /(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g, output : '<em>$1</em><em>$2</em><em>$3</em>' }, 
+// script tags	
+	{ input : /(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g, output : '<ins>$1</ins><ins>$2</ins><ins>$3</ins>' }, 
+// strings "" and attributes
+	{ input : /\"(.*?)(\"|<br>|<\/P>)/g, output : '<s>"$1$2</s>' }, 
+// ASP Comment
+	{ input : /\'(.*?)(\'|<br>|<\/P>)/g, output : '<dfn>\'$1$2</dfn>'}, 
+// <%.*
+	{ input : /(&lt;%)/g, output : '<strong>$1' }, 
+// .*%>	
+	{ input : /(%&gt;)/g, output : '$1</strong>' }, 
+// <%@...%>	
+	{ input : /(&lt;%@)(.+?)(%&gt;)/gi, output : '$1<span>$2</span>$3' }, 
+//Numbers	
+	{ input : /\b([\d]+)\b/g, output : '<var>$1</var>' }, 
+// Reserved Words 1 (Blue)
+	{ input : /\b(And|As|ByRef|ByVal|Call|Case|Class|Const|Dim|Do|Each|Else|ElseIf|Empty|End|Eqv|Exit|False|For|Function)\b/gi, output : '<a>$1</a>' }, 
+	{ input : /\b(Get|GoTo|If|Imp|In|Is|Let|Loop|Me|Mod|Enum|New|Next|Not|Nothing|Null|On|Option|Or|Private|Public|ReDim|Rem)\b/gi, output : '<a>$1</a>' }, 
+	{ input : /\b(Resume|Select|Set|Stop|Sub|Then|To|True|Until|Wend|While|With|Xor|Execute|Randomize|Erase|ExecuteGlobal|Explicit|step)\b/gi, output : '<a>$1</a>' }, 
+// Reserved Words 2 (Purple)	
+	{ input : /\b(Abandon|Abs|AbsolutePage|AbsolutePosition|ActiveCommand|ActiveConnection|ActualSize|AddHeader|AddNew|AppendChunk)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(AppendToLog|Application|Array|Asc|Atn|Attributes|BeginTrans|BinaryRead|BinaryWrite|BOF|Bookmark|Boolean|Buffer|Byte)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(CacheControl|CacheSize|Cancel|CancelBatch|CancelUpdate|CBool|CByte|CCur|CDate|CDbl|Charset|Chr|CInt|Clear)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(ClientCertificate|CLng|Clone|Close|CodePage|CommandText|CommandType|CommandTimeout|CommitTrans|CompareBookmarks|ConnectionString|ConnectionTimeout)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Contents|ContentType|Cookies|Cos|CreateObject|CreateParameter|CSng|CStr|CursorLocation|CursorType|DataMember|DataSource|Date|DateAdd|DateDiff)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(DatePart|DateSerial|DateValue|Day|DefaultDatabase|DefinedSize|Delete|Description|Double|EditMode|Eof|EOF|err|Error)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Exp|Expires|ExpiresAbsolute|Filter|Find|Fix|Flush|Form|FormatCurrency|FormatDateTime|FormatNumber|FormatPercent)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(GetChunk|GetLastError|GetRows|GetString|Global|HelpContext|HelpFile|Hex|Hour|HTMLEncode|IgnoreCase|Index|InStr|InStrRev)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Int|Integer|IsArray|IsClientConnected|IsDate|IsolationLevel|Join|LBound|LCase|LCID|Left|Len|Lock|LockType|Log|Long|LTrim)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(MapPath|MarshalOptions|MaxRecords|Mid|Minute|Mode|Month|MonthName|Move|MoveFirst|MoveLast|MoveNext|MovePrevious|Name|NextRecordset)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Now|Number|NumericScale|ObjectContext|Oct|Open|OpenSchema|OriginalValue|PageCount|PageSize|Pattern|PICS|Precision|Prepared|Property)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Provider|QueryString|RecordCount|Redirect|RegExp|Remove|RemoveAll|Replace|Requery|Request|Response|Resync|Right|Rnd)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(RollbackTrans|RTrim|Save|ScriptTimeout|Second|Seek|Server|ServerVariables|Session|SessionID|SetAbort|SetComplete|Sgn)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(Sin|Size|Sort|Source|Space|Split|Sqr|State|StaticObjects|Status|StayInSync|StrComp|String|StrReverse|Supports|Tan|Time)\b/gi, output : '<u>$1</u>' },
+	{ input : /\b(Timeout|Timer|TimeSerial|TimeValue|TotalBytes|Transfer|Trim|Type|Type|UBound|UCase|UnderlyingValue|UnLock|Update|UpdateBatch)\b/gi, output : '<u>$1</u>' }, 
+	{ input : /\b(URLEncode|Value|Value|Version|Weekday|WeekdayName|Write|Year)\b/gi, output : '<u>$1</u>' }, 
+// Reserved Words 3 (Turquis)
+	{ input : /\b(vbBlack|vbRed|vbGreen|vbYellow|vbBlue|vbMagenta|vbCyan|vbWhite|vbBinaryCompare|vbTextCompare)\b/gi, output : '<i>$1</i>' }, 
+  	{ input : /\b(vbSunday|vbMonday|vbTuesday|vbWednesday|vbThursday|vbFriday|vbSaturday|vbUseSystemDayOfWeek)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbFirstJan1|vbFirstFourDays|vbFirstFullWeek|vbGeneralDate|vbLongDate|vbShortDate|vbLongTime|vbShortTime)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbObjectError|vbCr|VbCrLf|vbFormFeed|vbLf|vbNewLine|vbNullChar|vbNullString|vbTab|vbVerticalTab|vbUseDefault|vbTrue)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbFalse|vbEmpty|vbNull|vbInteger|vbLong|vbSingle|vbDouble|vbCurrency|vbDate|vbString|vbObject|vbError|vbBoolean|vbVariant)\b/gi, output : '<i>$1</i>' }, 
+	{ input : /\b(vbDataObject|vbDecimal|vbByte|vbArray)\b/gi, output : '<i>$1</i>' },
+// html comments
+	{ input : /(&lt;!--.*?--&gt.)/g, output : '<big>$1</big>' } 
+]
+
+Language.Functions = [ 
+  	// Output at index 0, must be the desired tagname surrounding a $1
+	// Name is the index from the regex that marks the functionname
+	{input : /(function|sub)([ ]*?)(\w+)([ ]*?\()/gi , output : '<ins>$1</ins>', name : '$3'}
+]
+
+Language.snippets = [
+//Conditional
+	{ input : 'if', output : 'If $0 Then\n\t\nEnd If' },
+	{ input : 'ifelse', output : 'If $0 Then\n\t\n\nElse\n\t\nEnd If' },
+	{ input : 'case', output : 'Select Case $0\n\tCase ?\n\tCase Else\nEnd Select'},
+//Response
+	{ input : 'rw', output : 'Response.Write( $0 )' },
+	{ input : 'resc', output : 'Response.Cookies( $0 )' },
+	{ input : 'resb', output : 'Response.Buffer'},
+	{ input : 'resflu', output : 'Response.Flush()'},
+	{ input : 'resend', output : 'Response.End'},
+//Request
+	{ input : 'reqc', output : 'Request.Cookies( $0 )' },
+	{ input : 'rq', output : 'Request.Querystring("$0")' },
+	{ input : 'rf', output : 'Request.Form("$0")' },
+//FSO
+	{ input : 'fso', output : 'Set fso = Server.CreateObject("Scripting.FileSystemObject")\n$0' },
+	{ input : 'setfo', output : 'Set fo = fso.getFolder($0)' },
+	{ input : 'setfi', output : 'Set fi = fso.getFile($0)' },
+	{ input : 'twr', output : 'Set f = fso.CreateTextFile($0,true)\'overwrite\nf.WriteLine()\nf.Close'},
+	{ input : 'tre', output : 'Set f = fso.OpenTextFile($0, 1)\nf.ReadAll\nf.Close'},
+//Server
+	{ input : 'mapp', output : 'Server.Mappath($0)' },
+//Loops
+	{ input : 'foreach', output : 'For Each $0 in ?\n\t\nNext' },
+	{ input : 'for', output : 'For $0 to ? step ?\n\t\nNext' },
+	{ input : 'do', output : 'Do While($0)\n\t\nLoop' },
+	{ input : 'untilrs', output : 'do until rs.eof\n\t\nrs.movenext\nloop' },
+//ADO
+	{ input : 'adorec', output : 'Set rs = Server.CreateObject("ADODB.Recordset")' },
+	{ input : 'adocon', output : 'Set Conn = Server.CreateObject("ADODB.Connection")' },
+	{ input : 'adostr', output : 'Set oStr = Server.CreateObject("ADODB.Stream")' },
+//Http Request
+	{ input : 'xmlhttp', output : 'Set xmlHttp = Server.CreateObject("Microsoft.XMLHTTP")\nxmlHttp.open("GET", $0, false)\nxmlHttp.send()\n?=xmlHttp.responseText' },
+	{ input : 'xmldoc', output : 'Set xmldoc = Server.CreateObject("Microsoft.XMLDOM")\nxmldoc.async=false\nxmldoc.load(request)'},
+//Functions
+	{ input : 'func', output : 'Function $0()\n\t\n\nEnd Function'},
+	{ input : 'sub', output : 'Sub $0()\n\t\nEnd Sub'}
+
+]
+
+Language.complete = [
+	//{ input : '\'', output : '\'$0\'' },
+	{ input : '"', output : '"$0"' },
+	{ input : '(', output : '\($0\)' },
+	{ input : '[', output : '\[$0\]' },
+	{ input : '{', output : '{\n\t$0\n}' }		
+]
+
+Language.shortcuts = [
+	{ input : '[space]', output : '&nbsp;' },
+	{ input : '[enter]', output : '<br />' } ,
+	{ input : '[j]', output : 'testing' },
+	{ input : '[7]', output : '&amp;' }
+]
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/xsl.css
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/xsl.css	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,15 @@
+/*
+ * CodePress color styles for HTML syntax highlighting
+ * By RJ Bruneel
+ */
+ 
+b {color:#000080;} /* tags */
+ins, ins b, ins s, ins em {color:gray;} /* comments */
+s, s b {color:#7777e4;} /* attribute values */
+a {color:#E67300;} /* links */
+u {color:#CC66CC;} /* forms */
+big {color:#db0000;} /* images */
+em, em b {color:#800080;} /* style */
+strong {color:#800000;} /* script */
+tt i {color:darkblue;font-weight:bold;} /* script reserved words */
+xsl {color:green;} /* xsl */

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/xsl.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/codepress/languages/xsl.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,103 @@
+/*
+ * CodePress regular expressions for XSL syntax highlighting
+ * By RJ Bruneel
+ */
+
+Language.syntax = [ // XSL
+	{
+	input : /(&lt;[^!]*?&gt;)/g,
+	output : '<b>$1</b>' // all tags
+	},{
+	input : /(&lt;a.*?&gt;|&lt;\/a&gt;)/g,
+	output : '<a>$1</a>' // links
+	},{
+	input : /(&lt;img .*?&gt;)/g,
+	output : '<big>$1</big>' // images
+	},{
+	input : /(&lt;\/?(button|textarea|form|input|select|option|label).*?&gt;)/g,
+	output : '<u>$1</u>' // forms
+	},{
+	input : /(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g,
+	output : '<em>$1</em><em>$2</em><em>$3</em>' // style tags
+	},{
+	input : /(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g,
+	output : '<strong>$1</strong><tt>$2</tt><strong>$3</strong>' // script tags
+	},{	
+	input : /(&lt;xsl.*?&gt;|&lt;\/xsl.*?&gt;)/g,
+	output : '<xsl>$1</xsl>' // xsl
+	},{
+	input : /=(".*?")/g,
+	output : '=<s>$1</s>' // atributes double quote
+	},{
+	input : /=('.*?')/g,
+	output : '=<s>$1</s>' // atributes single quote
+	},{
+	input : /(&lt;!--.*?--&gt.)/g,
+	output : '<ins>$1</ins>' // comments 
+	},{
+	input : /\b(alert|window|document|break|continue|do|for|new|this|void|case|default|else|function|return|typeof|while|if|label|switch|var|with|catch|boolean|int|try|false|throws|null|true|goto)\b/g,
+	output : '<i>$1</i>' // script reserved words
+	}
+];
+
+Language.snippets = [
+	{input : 'aref', output : '<a href="$0"></a>' },
+	{input : 'h1', output : '<h1>$0</h1>' },
+	{input : 'h2', output : '<h2>$0</h2>' },
+	{input : 'h3', output : '<h3>$0</h3>' },
+	{input : 'h4', output : '<h4>$0</h4>' },
+	{input : 'h5', output : '<h5>$0</h5>' },
+	{input : 'h6', output : '<h6>$0</h6>' },
+	{input : 'html', output : '<html>\n\t$0\n</html>' },
+	{input : 'head', output : '<head>\n\t<meta http-equiv="content-type" content="text/html; charset=utf-8" />\n\t<title>$0</title>\n\t\n</head>' },
+	{input : 'img', output : '<img src="$0" width="" height="" alt="" border="0" />' },
+	{input : 'input', output : '<input name="$0" id="" type="" value="" />' },
+	{input : 'label', output : '<label for="$0"></label>' },
+	{input : 'legend', output : '<legend>\n\t$0\n</legend>' },
+	{input : 'link', output : '<link rel="stylesheet" href="$0" type="text/css" media="screen" charset="utf-8" />' },		
+	{input : 'base', output : '<base href="$0" />' }, 
+	{input : 'body', output : '<body>\n\t$0\n</body>' }, 
+	{input : 'css', output : '<link rel="stylesheet" href="$0" type="text/css" media="screen" charset="utf-8" />' },
+	{input : 'div', output : '<div>\n\t$0\n</div>' },
+	{input : 'divid', output : '<div id="$0">\n\t\n</div>' },
+	{input : 'dl', output : '<dl>\n\t<dt>\n\t\t$0\n\t</dt>\n\t<dd></dd>\n</dl>' },
+	{input : 'fieldset', output : '<fieldset>\n\t$0\n</fieldset>' },
+	{input : 'form', output : '<form action="$0" method="" name="">\n\t\n</form>' },
+	{input : 'meta', output : '<meta name="$0" content="" />' },
+	{input : 'p', output : '<p>$0</p>' },
+	{input : 'b', output : '<b>$0</b>' },
+	{input : 'li', output : '<li>$0</li>' },
+	{input : 'ul', output : '<ul>$0</ul>' },
+	{input : 'ol', output : '<ol>$0</ol>' },
+	{input : 'strong', output : '<strong>$0</strong>' },
+	{input : 'br', output : '<br />' },
+	{input : 'script', output : '<script type="text/javascript" language="javascript" charset="utf-8">\n\t$0\t\n</script>' },
+	{input : 'scriptsrc', output : '<script src="$0" type="text/javascript" language="javascript" charset="utf-8"></script>' },
+	{input : 'span', output : '<span>$0</span>' },
+	{input : 'table', output : '<table border="$0" cellspacing="" cellpadding="">\n\t<tr><th></th></tr>\n\t<tr><td></td></tr>\n</table>' },
+	{input : 'style', output : '<style type="text/css" media="screen">\n\t$0\n</style>' },
+	{input : 'xsl:stylesheet', output : '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' },
+	{input : 'xsl:template', output : '<xsl:template>$0</xsl:template>' },
+	{input : 'xsl:for-each', output : '<xsl:for-each select="$0"></xsl:for-each>' },
+	{input : 'xsl:choose', output : '<xsl:choose>$0<\xsl:choose>' },
+	{input : 'xsl:param', output : '<xsl:param name="$0" />' },
+	{input : 'xsl:variable', output : '<xsl:variable name="$0"></xsl:variable>' },
+	{input : 'xsl:if', output : '<xsl:if test="$0"></xsl:if>' },
+	{input : 'xsl:when', output : '<xsl:when test="$0"></xsl:when>' },
+	{input : 'xsl:otherwise', output : '<xsl:otherwise>$0</xsl:otherwise>' },
+	{input : 'xsl:attribute', output : '<xsl:attribute name="$0"></xsl:attribute>' },
+	{input : 'xsl:value-of', output : '<xsl:value-of select="$0"/>' },
+	{input : 'xsl:with-param', output : '<xsl:with-param name="$0" select="" />' },
+	{input : 'xsl:call-template', output : '<xsl:call-template name="$0">' }
+
+];
+	
+Language.complete = [ // Auto complete only for 1 character
+	{input : '\'',output : '\'$0\'' },
+	{input : '"', output : '"$0"' },
+	{input : '(', output : '\($0\)' },
+	{input : '[', output : '\[$0\]' },
+	{input : '{', output : '{\n\t$0\n}' }		
+];
+
+Language.shortcuts = [];
\ No newline at end of file

Added: jifty/branches/virtual-models/plugins/CodePress/share/web/static/js/codepress.js
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/plugins/CodePress/share/web/static/js/codepress.js	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,171 @@
+/*
+ * CodePress - Real Time Syntax Highlighting Editor written in JavaScript - http://codepress.org/
+ * 
+ * Copyright (C) 2006 Fernando M.A.d.S. <fermads at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the 
+ * GNU Lesser General Public License as published by the Free Software Foundation.
+ * 
+ * Read the full licence: http://www.opensource.org/licenses/lgpl-license.php
+ *
+ * This version is locally modified for Jifty, see Jifty::Plugin::CodePress
+ */
+
+CodePress = function(obj) {
+	var self = document.createElement('iframe');
+	self.textarea = obj;
+	self.textarea.disabled = true;
+	self.textarea.style.overflow = 'hidden';
+	self.style.height = self.textarea.clientHeight +'px';
+	self.style.width = self.textarea.clientWidth +'px';
+	self.textarea.style.overflow = 'auto';
+	self.style.border = '1px solid gray';
+	self.frameBorder = 0; // remove IE internal iframe border
+	self.style.visibility = 'hidden';
+	self.style.position = 'absolute';
+	self.options = self.textarea.className;
+	
+	self.initialize = function() {
+		self.editor = self.contentWindow.CodePress;
+		self.editor.body = self.contentWindow.document.getElementsByTagName('body')[0];
+		self.editor.setCode(self.textarea.value);
+		self.setOptions();
+		self.editor.syntaxHighlight('init');
+		self.textarea.style.display = 'none';
+		self.style.position = 'static';
+		self.style.visibility = 'visible';
+		self.style.display = 'inline';
+
+		// where blur event is delivered?
+		var iframe = self;
+		if (self.contentDocument) {          // For NS6
+			iframe = self.contentDocument;
+		} else if (self.contentWindow) {     // For IE5.5 and IE6
+			//iframe = self.contentWindow.document;
+		} else if (self.document) {          // For IE5
+			iframe = self.document;
+		} else {
+			alert("can't find frame");
+		}
+
+		DOM.Events.addListener( iframe, 'blur', function () {
+			self.textarea.value = self.getCode();
+			self.textarea.disabled = false;
+			return self;
+		});
+		DOM.Events.addListener( iframe, 'focus', function () {
+			self.textarea.disabled = true;
+			return self;
+		});
+	}
+	
+	// obj can by a textarea id or a string (code)
+	self.edit = function(obj,language) {
+		if(obj) self.textarea.value = document.getElementById(obj) ? document.getElementById(obj).value : obj;
+		if(!self.textarea.disabled) return;
+		self.language = language ? language : self.getLanguage();
+		self.src = '/static/codepress/codepress.html?language='+self.language+'&ts='+(new Date).getTime();
+		//if(self.attachEvent) self.attachEvent('onload',self.initialize);
+		//else self.addEventListener('load',self.initialize,false);
+		DOM.Events.addListener(self, 'load', self.initialize);
+	}
+
+	self.getLanguage = function() {
+		for (language in CodePress.languages) 
+			if(self.options.match('\\b'+language+'\\b')) 
+				return CodePress.languages[language] ? language : 'generic';
+		return 'generic';
+	}
+	
+	self.setOptions = function() {
+		if(self.options.match('autocomplete-off')) self.toggleAutoComplete();
+		if(self.options.match('readonly-on')) self.toggleReadOnly();
+		if(self.options.match('linenumbers-off')) self.toggleLineNumbers();
+	}
+	
+	self.getCode = function() {
+		return self.textarea.disabled ? self.editor.getCode() : self.textarea.value;
+	}
+
+	self.setCode = function(code) {
+		self.textarea.disabled ? self.editor.setCode(code) : self.textarea.value = code;
+	}
+
+	self.toggleAutoComplete = function() {
+		self.editor.autocomplete = (self.editor.autocomplete) ? false : true;
+	}
+	
+	self.toggleReadOnly = function() {
+		self.textarea.readOnly = (self.textarea.readOnly) ? false : true;
+		if(self.style.display != 'none') // prevent exception on FF + iframe with display:none
+			self.editor.readOnly(self.textarea.readOnly ? true : false);
+	}
+	
+	self.toggleLineNumbers = function() {
+		var cn = self.editor.body.className;
+		self.editor.body.className = (cn==''||cn=='show-line-numbers') ? 'hide-line-numbers' : 'show-line-numbers';
+	}
+	
+	self.toggleEditor = function() {
+		if(self.textarea.disabled) {
+			self.textarea.value = self.getCode();
+			self.textarea.disabled = false;
+			self.style.display = 'none';
+			self.textarea.style.display = 'inline';
+		}
+		else {
+			self.textarea.disabled = true;
+			self.setCode(self.textarea.value);
+			self.editor.syntaxHighlight('init');
+			self.style.display = 'inline';
+			self.textarea.style.display = 'none';
+		}
+	}
+
+	self.edit();
+	return self;
+}
+
+CodePress.languages = {	
+	csharp : 'C#', 
+	css : 'CSS', 
+	generic : 'Generic',
+	html : 'HTML',
+	java : 'Java', 
+	javascript : 'JavaScript', 
+	perl : 'Perl', 
+	ruby : 'Ruby',	
+	php : 'PHP', 
+	text : 'Text', 
+	sql : 'SQL',
+	vbscript : 'VBScript'
+}
+
+CodePress.instances = {}
+
+CodePress.run = function() {
+	t = document.getElementsByTagName('textarea');
+	for(var i=0,n=t.length;i<n;i++) {
+		if(t[i].className.match('codepress')) {
+			id = t[i].id;
+			//t[i].id = id+'_cp';
+			CodePress.instances[id] = new CodePress(t[i]);
+			t[i].parentNode.insertBefore(CodePress.instances[id], t[i]);
+		} 
+	}
+}
+
+CodePress.beforeSubmit = function() {
+	for (instance in CodePress.instances)  {
+		// consider just Jifty fields
+		if ( instance.substr(0,5) == 'J:A:F' ) {
+			CodePress.instances[ instance ].toggleEditor();
+		}
+	}
+}
+
+//if(window.attachEvent) window.attachEvent('onload',CodePress.run);
+//else window.addEventListener('DOMContentLoaded',CodePress.run,false);
+//DOM.Events.addListener(window, "load", CodePress.run);
+// Jifty-specific onLoad hook
+onLoadHook( 'CodePress.run();' );

Modified: jifty/branches/virtual-models/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html
==============================================================================
--- jifty/branches/virtual-models/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html	(original)
+++ jifty/branches/virtual-models/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html	Tue Jun 12 15:48:07 2007
@@ -19,7 +19,7 @@
 <h2><% _('Actions') %></h2>
 <ul>
 % foreach my $action (Jifty->api->actions) {
-% Jifty::Util->require($action);
+% next unless (Jifty::Util->try_to_require($action));
 % next if ( $action->can('autogenerated') and $action->autogenerated);
 <li><% Jifty->web->link( url => '/__jifty/admin/action/'.$action, label => $action) %></li>
 % }

Modified: jifty/branches/virtual-models/share/po/en.po
==============================================================================
--- jifty/branches/virtual-models/share/po/en.po	(original)
+++ jifty/branches/virtual-models/share/po/en.po	Tue Jun 12 15:48:07 2007
@@ -12,7 +12,7 @@
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
 #: lib/Jifty/Plugin/Authentication/Password/Notification/ConfirmLostPassword.pm:38

Modified: jifty/branches/virtual-models/share/po/zh_cn.po
==============================================================================
--- jifty/branches/virtual-models/share/po/zh_cn.po	(original)
+++ jifty/branches/virtual-models/share/po/zh_cn.po	Tue Jun 12 15:48:07 2007
@@ -27,6 +27,14 @@
 "\n"
 "%2\n"
 msgstr ""
+"\n"
+"您 (或自称是您的某人) 要求重设 %1 的密码.\n"
+"\n"
+"如果您不想重设密码,请忽略这封信\n"
+"\n"
+"若要重设密码,请点击以下链接:\n"
+"\n"
+"%2\n"
 
 #: lib/Jifty/Plugin/Authentication/Password/Notification/ConfirmEmail.pm:41
 #. ($appname,$confirm_url)
@@ -39,6 +47,12 @@
 "\n"
 "%2\n"
 msgstr ""
+"\n"
+"您(或自称是您的某人)在 %1 申请注册.\n"
+"\n"
+"请点击以下链接,以确认你的电子邮箱:\n"
+"\n"
+"%2\n"
 
 #: lib/Jifty/Action/Record/Search.pm:125
 msgid "!=>< allowed"
@@ -47,7 +61,7 @@
 #: lib/Jifty/Notification.pm:94
 #. ($appname, Jifty->config->framework('AdminEmail')
 msgid "%1 <%2>"
-msgstr ""
+msgstr "%1 <%2>"
 
 #: lib/Jifty/Action/Record/Search.pm:115
 #. ($label)
@@ -67,7 +81,7 @@
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:141
 #. ($collection-> count)
 msgid "%1 entries"
-msgstr "%1 条记录"
+msgstr "共 %1 条记录"
 
 #: lib/Jifty/Action/Record/Search.pm:123
 #. ($label)
@@ -126,16 +140,16 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ConfirmEmail.pm:58
 msgid ". Your email address has now been confirmed."
-msgstr ""
+msgstr ". 现已成功确认您的电子邮箱."
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/SendPasswordReminder.pm:96
 msgid "A link to reset your password has been sent to your email account."
-msgstr ""
+msgstr "重设密码的链接已发送到您的电子邮箱."
 
 #: lib/Jifty/Notification.pm:96
 #. ($appname)
 msgid "A notification from %1!"
-msgstr ""
+msgstr "从 %1 送来的消息!"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:443
 msgid "Actions"
@@ -143,7 +157,7 @@
 
 #: lib/Jifty/Plugin/SkeletonApp/Dispatcher.pm:28
 msgid "Administration"
-msgstr "管理接口"
+msgstr "管理界面"
 
 #: lib/Jifty/View/Declare/Helpers.pm:363 share/web/templates/_elements/wrapper:11
 msgid "Administration mode is enabled."
@@ -163,7 +177,7 @@
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:131
 msgid "Anyway, the software has logged this error."
-msgstr ""
+msgstr "系统已记录该错误."
 
 #: lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm:19
 msgid "Authentication token"
@@ -179,7 +193,7 @@
 
 #: share/web/templates/__jifty/halo:117
 msgid "Bindings"
-msgstr "快速键"
+msgstr "快捷键"
 
 #: share/web/templates/helpers/calendar.html:4
 #. (_ &><body class="calpopup"><a href="#" onclick="window.close()
@@ -250,15 +264,15 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:23 lib/Jifty/Plugin/Authentication/Password/Action/SendPasswordReminder.pm:32
 msgid "Email"
-msgstr ""
+msgstr "电子邮箱"
 
 #: lib/Jifty/Plugin/User/Mixin/Model/User.pm:33
 msgid "Email address"
-msgstr ""
+msgstr "电子邮箱"
 
 #: lib/Jifty/Plugin/User/Mixin/Model/User.pm:35
 msgid "Email address confirmed?"
-msgstr ""
+msgstr "已确认电子邮箱?"
 
 #: lib/Jifty/Action.pm:1158
 msgid "Foo cannot contain -, *, +, or ?."
@@ -291,7 +305,7 @@
 #: lib/Jifty/I18N.pm:19 lib/Jifty/I18N.pm:23
 #. ('World')
 msgid "Hello, %1!"
-msgstr ""
+msgstr "%1, 您好!"
 
 #: lib/Jifty/Plugin/SkeletonApp/View.pm:31 share/web/templates/_elements/sidebar:5
 #. ($u->$method()
@@ -304,7 +318,7 @@
 
 #: lib/Jifty/Plugin/User/Mixin/Model/User.pm:30
 msgid "How should I display your name to other users?"
-msgstr ""
+msgstr "您在本系统的名字"
 
 #: lib/Jifty/Action.pm:1090
 msgid "I changed $field for you"
@@ -312,15 +326,15 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:64
 msgid "I'm not sure how this happened."
-msgstr ""
+msgstr "我也不知道怎么回事."
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:62
 msgid "Internal error"
-msgstr ""
+msgstr "内部错误"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm:78 lib/Jifty/Plugin/Authentication/Password/Action/SendPasswordReminder.pm:79
 msgid "It doesn't look like there's an account by that name."
-msgstr ""
+msgstr "该账号不存在."
 
 #: lib/Jifty/Plugin/User/Mixin/Model/User.pm:89
 msgid "It looks like somebody else is using that address. Is there a chance you have another account?"
@@ -356,23 +370,23 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm:106
 msgid "Login"
-msgstr ""
+msgstr "登录"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:55
 msgid "Login with a password"
-msgstr ""
+msgstr "使用密码登录"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:40
 msgid "Login!"
-msgstr ""
+msgstr "登录!"
 
 #: lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm:123
 msgid "Logout"
-msgstr ""
+msgstr "注销"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:63
 msgid "Lost your password?"
-msgstr ""
+msgstr "忘记密码?"
 
 #:
 msgid "Manage %1 records"
@@ -380,12 +394,12 @@
 
 #:
 msgid "Manage records:"
-msgstr "管理记录:"
+msgstr "管理记录:"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:46 lib/Jifty/Plugin/AdminUI/View-not-yet.pm:473
 #. ($object_type)
 msgid "Manage records: [_1]"
-msgstr ""
+msgstr "管理记录: [_1]"
 
 #: share/web/templates/__jifty/error/mason_internal_error:1
 msgid "Mason error"
@@ -397,7 +411,7 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:81
 msgid "New password"
-msgstr ""
+msgstr "新密码"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:172
 msgid "Next Page"
@@ -405,11 +419,11 @@
 
 #: lib/Jifty/Plugin/User/Mixin/Model/User.pm:29
 msgid "Nickname"
-msgstr ""
+msgstr "昵称"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:52
 msgid "No account yet? It's quick and easy. "
-msgstr ""
+msgstr "还没有账号?您可以方便快捷地注册. "
 
 #: lib/Jifty/Action/Record/Search.pm:130
 msgid "No field contains"
@@ -425,11 +439,11 @@
 
 #:
 msgid "Online Documentation"
-msgstr "线上文件"
+msgstr "在线文件"
 
 #: lib/Jifty/Plugin/OnlineDocs/Dispatcher.pm:26
 msgid "Online docs"
-msgstr "线上文件"
+msgstr "在线文件"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:134
 #. ($page, $collection->pager->last_page)
@@ -442,15 +456,15 @@
 
 #: share/web/templates/__jifty/halo:72
 msgid "Parent"
-msgstr "上层组件"
+msgstr "父组件"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:29 lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:32 lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm:26
 msgid "Password"
-msgstr ""
+msgstr "密码"
 
 #: lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm:88
 msgid "Passwords need to be at least six characters long"
-msgstr ""
+msgstr "密码长度至少为6位"
 
 #: lib/Jifty/Record.pm:272 lib/Jifty/Record.pm:351 lib/Jifty/Record.pm:70
 msgid "Permission denied"
@@ -458,15 +472,15 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:66
 msgid "Please email us!"
-msgstr ""
+msgstr "请发邮件通知我们!"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:163
 msgid "Previous Page"
-msgstr "上一页"
+msgstr "前一页"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:65
 msgid "Really, really sorry."
-msgstr ""
+msgstr "非常非常抱歉"
 
 #:
 msgid "Record created"
@@ -474,16 +488,16 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:37
 msgid "Remember me?"
-msgstr ""
+msgstr "记住我?"
 
 #: share/web/templates/__jifty/halo:69
 #. ($frame->{'render_time'})
 msgid "Rendered in %1s"
-msgstr "页面绘制时间: %1 秒"
+msgstr "页面生成时间: %1 秒"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:78
 msgid "Reset lost password"
-msgstr ""
+msgstr "重设密码"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:53
 msgid "Run the action"
@@ -491,7 +505,7 @@
 
 #: share/web/templates/__jifty/halo:111
 msgid "SQL Statements"
-msgstr "SQL 陈述式"
+msgstr "SQL 语句"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:327
 msgid "Save"
@@ -499,7 +513,7 @@
 
 #:
 msgid "Schema"
-msgstr "模式"
+msgstr "纲要"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:304
 msgid "Search"
@@ -507,27 +521,27 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:119
 msgid "Send"
-msgstr ""
+msgstr "发送"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:97
 msgid "Send a link to reset your password"
-msgstr ""
+msgstr "发送重设密码的链接"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:109 lib/Jifty/Plugin/Authentication/Password/View.pm:114
 msgid "Send a password reminder"
-msgstr ""
+msgstr "发送密码提示"
 
 #: lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm:111
 msgid "Sign up"
-msgstr ""
+msgstr "注册"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:53
 msgid "Sign up for an account!"
-msgstr ""
+msgstr "现在就注册!"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:30 lib/Jifty/Plugin/Authentication/Password/View.pm:35
 msgid "Signup"
-msgstr ""
+msgstr "注册"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Signup.pm:108
 #. ($msg)
@@ -544,7 +558,7 @@
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:132
 msgid "Sorry about this."
-msgstr ""
+msgstr "非常抱歉"
 
 #:
 msgid "Table of Contents"
@@ -553,7 +567,7 @@
 #: lib/Jifty/Plugin/User/Mixin/Model/User.pm:82
 #. ($new_email)
 msgid "That %1 doesn't look like an email address."
-msgstr ""
+msgstr "%1 不是合法的电子邮箱"
 
 #: lib/Jifty/Action.pm:878
 msgid "That doesn't look like a correct value"
@@ -561,7 +575,7 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm:71 lib/Jifty/Plugin/Authentication/Password/Action/SendPasswordReminder.pm:72 lib/Jifty/Plugin/Authentication/Password/Action/Signup.pm:70
 msgid "That doesn't look like an email address."
-msgstr ""
+msgstr "这不是合法的电子邮箱."
 
 #: lib/Jifty/Action/Record.pm:249
 msgid "That doesn't look right, but I don't know why"
@@ -569,7 +583,7 @@
 
 #: lib/Jifty/Action/Record.pm:181
 msgid "The passwords you typed didn't match each other"
-msgstr "两组口令不匹配."
+msgstr "两组密码不匹配."
 
 #: lib/Jifty/Web.pm:365
 msgid "There was an error completing the request.  Please try again later."
@@ -577,7 +591,7 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:81
 msgid "There was an error setting your password."
-msgstr ""
+msgstr "设置密码时出现错误."
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:37
 msgid "There's a pretty good chance that error message doesn't mean anything to you, but we'd rather you have a little bit of information about what went wrong than nothing. We've logged this error, so we know we need to write something friendly explaining just what happened and how to fix it."
@@ -585,7 +599,7 @@
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:419
 msgid "This console lets you manage the records in your Jifty database. Below, you should see a list of all your database tables. Feel free to go through and add, delete or modify records."
-msgstr "您可利用此界面来管理数据库的内容. 请点选表格名称, 进行增删及编辑."
+msgstr "您可利用此界面来管理数据库的内容. 请选择表格名称, 进行增删及编辑."
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:425
 msgid "To disable this administrative console, add \"AdminMode: 0\" under the \"framework:\" settings in the config file (etc/config.yml)."
@@ -593,19 +607,19 @@
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:127
 msgid "Toggle search"
-msgstr "切换搜寻画面"
+msgstr "切换搜索页面"
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:173 share/web/templates/__jifty/error/mason_internal_error:6
 msgid "Try again"
-msgstr "重试一下"
+msgstr "重试"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Signup.pm:108
 msgid "Try again later. We're really, really sorry."
-msgstr ""
+msgstr "请稍后再试,抱歉."
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Signup.pm:53
 msgid "Type that again?"
-msgstr ""
+msgstr "请再输入一次"
 
 #: lib/Jifty/Action/Record/Update.pm:156
 msgid "Updated"
@@ -613,7 +627,7 @@
 
 #: share/web/templates/__jifty/halo:93
 msgid "Variables"
-msgstr "变項"
+msgstr "变项"
 
 #: lib/Jifty.pm:27
 msgid "W00t"
@@ -621,52 +635,52 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Signup.pm:114
 msgid "We've sent a confirmation message to your email box."
-msgstr ""
+msgstr "系统已将确认信发送到您的电子邮箱."
 
 #: lib/Jifty/I18N.pm:31
 #. ('Bob', 'World')
 msgid "Welcome %1 to the %2"
-msgstr ""
+msgstr "%2, 欢迎来到 %1"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:181
 #. ($user->name)
 msgid "Welcome back, %1."
-msgstr ""
+msgstr "欢迎回来, %1."
 
 #: lib/Jifty/Plugin/Authentication/Password/Notification/ConfirmEmail.pm:40
 #. ($appname)
 msgid "Welcome to %1!"
-msgstr ""
+msgstr "欢迎来到 %1!"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Signup.pm:113
 #. (Jifty->config->framework('ApplicationName')
 msgid "Welcome to %1, %2."
-msgstr ""
+msgstr "%2, 欢迎来到 %1"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ConfirmEmail.pm:57
 #. (Jifty->config->framework('ApplicationName')
 msgid "Welcome to %1, %2. "
-msgstr ""
+msgstr "%2, 欢迎来到 %1"
 
 #: lib/Jifty/Plugin/SkeletonApp/View.pm:75 share/web/templates/index.html:1
 msgid "Welcome to your new Jifty application"
-msgstr "欢迎光临您的崭新的 Jifty 应用程序"
+msgstr "欢迎光临您崭新的 Jifty 应用程序"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:63
 msgid "You don't exist."
-msgstr ""
+msgstr "该帐号不存在."
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:130
 msgid "You got to a page that we don't think exists."
-msgstr ""
+msgstr "此页面不存在."
 
 #: share/web/templates/dhandler:5
 msgid "You got to a page that we don't think exists.  Anyway, the software has logged this error. Sorry about this."
-msgstr "抱歉, 此页面不存在, 系统已留下纪录."
+msgstr "抱歉, 此页面不存在, 系统已记录该错误."
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ConfirmEmail.pm:44
 msgid "You have already confirmed your account."
-msgstr ""
+msgstr "您已经确认过该账号了."
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:98
 msgid "You lost your password. A link to reset it will be sent to the following email address:"
@@ -678,11 +692,11 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:152
 msgid "You may have mistyped your email address or password. Give it another shot."
-msgstr ""
+msgstr "您或许输错了电子邮箱或密码,请重试."
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:146
 msgid "You may have mistyped your email or password. Give it another shot."
-msgstr ""
+msgstr "您或许输错了电子邮箱或密码,请重试."
 
 #: lib/Jifty/Action.pm:865
 msgid "You need to fill in this field"
@@ -691,11 +705,11 @@
 #: lib/Jifty/Plugin/SkeletonApp/View.pm:75 share/web/templates/index.html:3
 #. ('http://hdl.loc.gov/loc.pnp/cph.3c13461')
 msgid "You said you wanted a pony. (Source %1)"
-msgstr "您可不正是想要一匹小马吗? (参见 %1)"
+msgstr "您不正想要一匹小马吗? (参见 %1)"
 
 #: lib/Jifty/Plugin/Authentication/Password/View.pm:69
 msgid "You're already logged in."
-msgstr ""
+msgstr "您已经登录了."
 
 #: lib/Jifty/Plugin/SkeletonApp/View.pm:34 share/web/templates/_elements/sidebar:7
 msgid "You're not currently signed in."
@@ -703,15 +717,15 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/Login.pm:38
 msgid "Your browser can remember your login for you"
-msgstr ""
+msgstr "下次开启浏览器时, 是否保持登录状态?"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:87
 msgid "Your password has been reset.  Welcome back."
-msgstr ""
+msgstr "您的密码已被重设. 欢迎回来."
 
 #: lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm:28
 msgid "Your password should be at least six characters"
-msgstr ""
+msgstr "密码长度至少为6位"
 
 #: lib/Jifty/Plugin/AdminUI/View-not-yet.pm:246
 msgid "asc"
@@ -723,7 +737,7 @@
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/SendAccountConfirmation.pm:31
 msgid "email address"
-msgstr ""
+msgstr "电子邮箱"
 
 #: lib/Jifty/Plugin/ErrorTemplates/View.pm:46
 msgid "for now, and try to forget that we let you down."
@@ -731,16 +745,16 @@
 
 #: lib/Jifty/Manual/PageRegions.pod:188
 msgid "text of the link"
-msgstr "连结文字"
+msgstr "链接文字"
 
 #: lib/Jifty/Manual/PageRegions.pod:225
 msgid "text of the link that hides"
-msgstr "隐藏连结文字"
+msgstr "隐藏链接文字"
 
 #: lib/Jifty/Plugin/Authentication/Password/Action/ResetLostPassword.pm:37
 msgid "type your password again"
-msgstr ""
+msgstr "重新输入密码"
 
 #: lib/Jifty/Action.pm:1050
 msgid "warning"
-msgstr ""
+msgstr "警告"

Modified: jifty/branches/virtual-models/share/web/static/js/calendar.js
==============================================================================
--- jifty/branches/virtual-models/share/web/static/js/calendar.js	(original)
+++ jifty/branches/virtual-models/share/web/static/js/calendar.js	Tue Jun 12 15:48:07 2007
@@ -82,6 +82,12 @@
             month = args[0][0][1],
             day   = args[0][0][2];
 
+        if (month < 10)
+            month = "0" + month;
+
+        if (day < 10)
+            day = "0" + day;
+
         var input = obj.event.target;
         
         input.value = year + "-" + month + "-" + day;

Modified: jifty/branches/virtual-models/share/web/static/js/jifty.js
==============================================================================
--- jifty/branches/virtual-models/share/web/static/js/jifty.js	(original)
+++ jifty/branches/virtual-models/share/web/static/js/jifty.js	Tue Jun 12 15:48:07 2007
@@ -121,7 +121,11 @@
                     a['fields'][Form.Element.getField(f)] = {};
                 var field = Form.Element.getField(f);
                 var type = Form.Element.getType(f);
-                    
+
+                // XXX: fallback value being an array makes server
+                // upset, we don't think that should happen anyway
+                if (type == 'fallback' && a['fields'][field][type])
+                    continue                    
                 a['fields'][field][type] = this._mergeValues(a['fields'][field][type],
                                                              Form.Element.getValue(f));
             }
@@ -686,9 +690,11 @@
 	    } else {
 		Element.update(element, textContent.stripScripts());
 	    }
-	    // We need to give the browser some "settle" time before we eval scripts in the body
-	    setTimeout((function() { this.evalScripts() }).bind(textContent), 10);
-	    Behaviour.apply(element);
+	    // We need to give the browser some "settle" time before
+	    // we eval scripts in the body
+	    YAHOO.util.Event.onAvailable(element.id, function() {
+		    (function() { this.evalScripts() }).bind(textContent)();
+		    Behaviour.apply(element) });
 	}
     }
     dom_fragment.setArgs(new_dom_args);
@@ -719,7 +725,6 @@
 //     - 'mode' is one of 'Replace', or the name of a Prototype Insertion
 //     - 'effect' is the name of a Prototype Effect
 function update() {
-    // If we don't have XMLHttpRequest, bail and fallback on full-page
     // loads
     if(!Ajax.getTransport()) return true;
     // XXX: prevent default behavior in IE
@@ -740,11 +745,29 @@
     // Grab extra arguments (from a button)
     var button_args = Form.Element.buttonFormElements(trigger);
 
+    var form = Form.Element.getForm(trigger);
+    // If the action is null, take all actions
+    if (named_args['actions'] == null) {
+        named_args['actions'] = {};
+        // default to disable fields
+        if (form)
+            Form.getActions(form).map(function(x){
+                named_args['actions'][x.moniker] = 1;
+            });
+    }
+    var optional_fragments;
+    if (form && form['J:CALL']) 
+	optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': null}) ];
     // Build actions structure
     request['actions'] = $H();
     for (var moniker in named_args['actions']) {
         var disable = named_args['actions'][moniker];
         var a = new Action(moniker, button_args);
+        // Special case for Redirect, allow optional, implicit __page
+        // from the response to be used.
+        if (a.actionClass == 'Jifty::Action::Redirect')
+            optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': a.fields().last().value}) ];
+
         if (a.register) {
             if (a.hasUpload())
                 return true;
@@ -778,13 +801,14 @@
         // Grab the XML response
         var response = transport.responseXML.documentElement;
         // Loop through the result looking for it
+        var expected_fragments = optional_fragments ? optional_fragments : named_args['fragments'];
         for (var response_fragment = response.firstChild;
              response_fragment != null && response_fragment.nodeName == 'fragment';
              response_fragment = response_fragment.nextSibling) {
 
             var f; 
-            for (var i = 0; i < named_args['fragments'].length; i++) {
-                f = named_args['fragments'][i];
+            for (var i = 0; i < expected_fragments.length; i++) {
+                f = expected_fragments[i];
                 if (response_fragment.getAttribute("id") == f['region'])
                     break;
             }
@@ -806,6 +830,13 @@
                 }
             }
         }
+        for (var redirect = response.firstChild;
+             redirect != null;
+             redirect = redirect.nextSibling) {
+            if (redirect.nodeName == 'redirect') {
+                document.location =  redirect.firstChild.firstChild.nodeValue;
+            }
+        }
     };
     var onFailure = function(transport, object) {
         hide_wait_message_now();
@@ -829,6 +860,9 @@
         request['variables']['region-'+k] = current_args[k];
     }
 
+    // Build continuation structure
+    request['continuation'] = named_args['continuation'];
+
     // Push any state variables which we set into the forms
     for (var i = 0; i < document.forms.length; i++) {
         var form = document.forms[i];

Modified: jifty/branches/virtual-models/share/web/static/js/key_bindings.js
==============================================================================
--- jifty/branches/virtual-models/share/web/static/js/key_bindings.js	(original)
+++ jifty/branches/virtual-models/share/web/static/js/key_bindings.js	Tue Jun 12 15:48:07 2007
@@ -24,6 +24,10 @@
         DOM.Events.removeListener(Jifty.KeyBindings.listener);
     },
 
+    reset: function() {
+        Jifty.KeyBindings.bindings = new Array();
+    },
+
     doClick: function(e) {
         if (e.target.nodeType == 3) // defeat Safari bug
             e.target = e.target.parentNode;

Modified: jifty/branches/virtual-models/share/web/templates/__jifty/autocomplete.xml
==============================================================================
--- jifty/branches/virtual-models/share/web/templates/__jifty/autocomplete.xml	(original)
+++ jifty/branches/virtual-models/share/web/templates/__jifty/autocomplete.xml	Tue Jun 12 15:48:07 2007
@@ -3,6 +3,10 @@
 # behavior is accomplished inside the framework.  It will go away once we
 # have infrastructure for serving things of various content-types.
 $r->content_type('text/xml; charset=UTF-8');
+unless (Jifty->web->response->result('autocomplete')) {
+  print "<body />";
+  return;
+}
 my $ref =  Jifty->web->response->result('autocomplete')->content;
 my @options = @{$ref->{'completions'}||[]};
 </%init>

Modified: jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml
==============================================================================
--- jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml	(original)
+++ jifty/branches/virtual-models/share/web/templates/__jifty/webservices/xml	Tue Jun 12 15:48:07 2007
@@ -3,6 +3,16 @@
 my $writer = XML::Writer->new( OUTPUT => \$output, UNSAFE => 1 );
 $writer->xmlDecl( "UTF-8", "yes" );
 $writer->startTag("response");
+
+if (my $ext = Jifty->web->request->argument('_webservice_external_redirect')) {
+    $writer->startTag("redirect");
+    $writer->cdataElement(url=> $ext);
+
+    $writer->endTag();
+}
+else {
+
+FRAGMENT:
 for my $f ( Jifty->web->request->fragments ) {
     # Set up the region stack
     local Jifty->web->{'region_stack'} = [];
@@ -25,11 +35,16 @@
 
         $new ||= Jifty::Web::PageRegion->new(
             name           => $f->name,
-            path           => $f->path,
+            path           => URI::Escape::uri_unescape($f->path),
             region_wrapper => $f->wrapper,
             parent         => Jifty->web->current_region,
             defaults       => \%defaults,
         );
+
+        # It's possible that the pageregion creation could fail -- no
+        # name, for instance.  In that case, bail on this fragment.
+        next FRAGMENT unless $new;
+
         $new->enter;
     }
 
@@ -43,6 +58,8 @@
     Jifty->web->current_region->exit while Jifty->web->current_region;
 }
 
+}
+
 my %results = Jifty->web->response->results;
 for (keys %results) {
     $writer->startTag("result", moniker => $_, class => $results{$_}->action_class);

Modified: jifty/branches/virtual-models/share/web/templates/_elements/sidebar
==============================================================================
--- jifty/branches/virtual-models/share/web/templates/_elements/sidebar	(original)
+++ jifty/branches/virtual-models/share/web/templates/_elements/sidebar	Tue Jun 12 15:48:07 2007
@@ -1,8 +1,7 @@
 <div id="salutation">
-% if (Jifty->web->current_user->id and Jifty->web->current_user->user_object) {
-% my $u = Jifty->web->current_user->user_object;
-% my $method = $u->_brief_description;
-<%_('Hiya, %1.',$u->$method())%>
+% my $u = Jifty->web->current_user;
+% if ($u->id and $u->user_object) {
+<%_('Hiya, %1.',$u->username)%>
 % }  else {
 <%_("You're not currently signed in.")%>
 % }

Modified: jifty/branches/virtual-models/t/01-dependencies.t
==============================================================================
--- jifty/branches/virtual-models/t/01-dependencies.t	(original)
+++ jifty/branches/virtual-models/t/01-dependencies.t	Tue Jun 12 15:48:07 2007
@@ -66,7 +66,7 @@
 
 for (sort keys %required) {
     my $first_in = Module::CoreList->first_release($_, $required{$_});
-    fail("Required module $_ is already in core") if defined $first_in and $first_in <= 5.00803;
+    fail("Required module $_ (v. $required{$_}) is in core since $first_in") if defined $first_in and $first_in <= 5.008003;
 }
 
 1;

Added: jifty/branches/virtual-models/t/TestApp-Plugin-News/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-News/Makefile.PL	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,7 @@
+use inc::Module::Install;
+
+name        'TestApp::Plugin::News';
+version     '0.01';
+requires    'Jifty' => '0.70422';
+
+WriteAll;

Added: jifty/branches/virtual-models/t/TestApp-Plugin-News/bin/jifty
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-News/bin/jifty	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,15 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use File::Basename qw(dirname); 
+use UNIVERSAL::require;
+
+BEGIN {
+    Jifty::Util->require or die $UNIVERSAL::require::ERROR;
+    my $root = Jifty::Util->app_root;
+    unshift @INC, "$root/lib" if ($root);
+}
+
+use Jifty::Script;
+local $SIG{INT} = sub { warn "Stopped\n"; exit; };
+Jifty::Script->dispatch();

Added: jifty/branches/virtual-models/t/TestApp-Plugin-News/etc/config.yml
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-News/etc/config.yml	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,63 @@
+--- 
+framework: 
+  AdminMode: 1
+  ApplicationClass: TestApp::Plugin::News
+  ApplicationName: TestApp-Plugin-News
+  ApplicationUUID: 09757060-0564-11DC-AD52-CB2C768159CC
+  Database: 
+    CheckSchema: 1
+    Database: testapp_plugin_news
+    Driver: SQLite
+    Host: localhost
+    Password: ''
+    RecordBaseClass: Jifty::DBI::Record::Cachable
+    User: ''
+    Version: 0.0.1
+  DevelMode: 1
+  L10N: 
+    PoDir: share/po
+  LogLevel: INFO
+  Mailer: Sendmail
+  MailerArgs: []
+
+  Plugins: 
+    - 
+      SkeletonApp: {}
+
+    - 
+      REST: {}
+
+    - 
+      Halo: {}
+
+    - 
+      ErrorTemplates: {}
+
+    - 
+      OnlineDocs: {}
+
+    - 
+      CompressedCSSandJS: {}
+
+    - 
+      AdminUI: {}
+
+  PubSub: 
+    Backend: Memcached
+    Enable: ~
+  SkipAccessControl: 0
+  TemplateClass: TestApp::Plugin::News::View
+  Web: 
+    BaseURL: http://localhost
+    DataDir: var/mason
+    Globals: []
+
+    MasonConfig: 
+      autoflush: 0
+      default_escape_flags: h
+      error_format: text
+      error_mode: fatal
+    Port: 8888
+    ServeStaticFiles: 1
+    StaticRoot: share/web/static
+    TemplateRoot: share/web/templates

Added: jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Model/News.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/Model/News.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,16 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::News::Model::News;
+use Jifty::DBI::Schema;
+
+# Mixins
+
+use TestApp::Plugin::News::Record schema {
+
+};
+
+use  Jifty::Plugin::SiteNews::Mixin::Model::News;
+
+1;
+

Added: jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-News/lib/TestApp/Plugin/News/View.pm	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,20 @@
+use warnings;
+use strict;
+
+=head1 NAME
+
+TestApp::Plugin::News::View
+
+=head1 DESCRIPTION
+
+
+=cut
+
+package TestApp::Plugin::News::View;
+use Jifty::View::Declare -base;
+
+use Jifty::Plugin::SiteNews::View::News;
+alias Jifty::Plugin::SiteNews::View::News under '/news/';
+
+
+1;

Modified: jifty/branches/virtual-models/t/TestApp/lib/TestApp/Dispatcher.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/lib/TestApp/Dispatcher.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp/lib/TestApp/Dispatcher.pm	Tue Jun 12 15:48:07 2007
@@ -63,4 +63,23 @@
 on '/on_stage_show' => run { show '/index.html'; };
 after '/after_stage_show' => run { show '/index.html'; };
 
+
+
+before 'pre-redir-region' => run {
+    redirect('/post-redir-region');
+
+};
+
+before '__jifty/webservices/*' => run {
+    my (@actions) = grep { $_->class eq 'Jifty::Action::Redirect' } values %{ Jifty->web->request->{'actions'} };
+    $_->active(0) for @actions;
+};
+
+on qr{(__jifty/webservices/.*)} => run {
+    use Data::Dumper;
+    for $act (@actions) {
+	warn Dumper($act);
+    }
+};
+
 1;

Modified: jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp/lib/TestApp/Model/User.pm	Tue Jun 12 15:48:07 2007
@@ -38,9 +38,6 @@
 
 
 # Your model-specific methods go here.
-sub current_user_can {
-    return 1;
-}
 
 1;
 

Modified: jifty/branches/virtual-models/t/TestApp/lib/TestApp/View.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/lib/TestApp/View.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp/lib/TestApp/View.pm	Tue Jun 12 15:48:07 2007
@@ -44,8 +44,8 @@
 
 require TestApp::View::base;
 require TestApp::View::instance;
-import_templates TestApp::View::base under '/base';
-import_templates TestApp::View::instance under '/instance';
+alias TestApp::View::base under '/base';
+alias TestApp::View::instance under '/instance';
 
 use Jifty::View::Declare::CRUD;
 
@@ -65,5 +65,42 @@
     };
 };
 
+template '/foo/list' => sub {
+    outs('list!');
+    show('/foo/item', { id => 1 } );
+    show('/foo/item', { id => 2 } );
+    render_region('special', path => '/foo/item', defaults => { id => 3 } );
+};
+
+template '/foo/item' => sub {
+    my ($self, $args) = @_;
+    span { $args->{id} }
+};
+
+
+template 'region-with-internal-redirect' => page {
+    
+    h1 { 'outer page'};
+
+    render_region('internal', path => '/pre-redir-region');
+    render_region('internal2', path => '/nonredir-region');
+    
+
+    h2 { 'still going'} ;
+};
+
 
+template 'nonredir-region' => sub {
+    h1 { 'other region'};
+};
+
+template 'pre-redir-region' => sub {
+    h1 { 'sorry. no.'};
+};
+
+
+template 'post-redir-region' => sub {
+
+    h1 { 'redirected ok'};
+};
 1;

Modified: jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/base.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/base.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/base.pm	Tue Jun 12 15:48:07 2007
@@ -1,11 +1,13 @@
 package TestApp::View::base;
 use Jifty::View::Declare -base;
 
+sub base_path { '/base' }
+
 template 'list_ht' => sub {
     my $self = shift;
     outs("/base/list=$self");
     for (0..1) {
-	$self->has_template('view')->();
+	$self->resolve_template('view')->();
     }
 };
 
@@ -13,7 +15,7 @@
     my $self = shift;
     outs("/base/list=$self");
     for (0..1) {
-	show('view');
+	show($self->base_path.'/view');
     }
 };
 
@@ -21,7 +23,7 @@
     my $self = shift;
     outs("/base/list=$self");
     for (0..1) {
-	render_region('view-'.$_, path => 'view');
+	render_region('view-'.$_, path => $self->base_path.'/view');
     }
 };
 

Modified: jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/instance.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/instance.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp/lib/TestApp/View/instance.pm	Tue Jun 12 15:48:07 2007
@@ -2,6 +2,8 @@
 use Jifty::View::Declare -base;
 use base 'TestApp::View::base';
 
+sub base_path { '/instance' }
+
 template 'myview' => sub {
     
 };

Modified: jifty/branches/virtual-models/t/TestApp/t/09-redirect.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/09-redirect.t	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/09-redirect.t	Tue Jun 12 15:48:07 2007
@@ -10,8 +10,8 @@
 
 =cut
 
-BEGIN {chdir "t/TestApp"}
-use lib '../../lib';
+use lib 't/lib';
+use Jifty::SubTest;
 use Jifty::Test tests => 6;
 use Jifty::Test::WWW::Mechanize;
 

Modified: jifty/branches/virtual-models/t/TestApp/t/11-current_user.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/11-current_user.t	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/11-current_user.t	Tue Jun 12 15:48:07 2007
@@ -21,6 +21,9 @@
 my $system_user = TestApp::CurrentUser->superuser;
 ok($system_user, "Found a system user");
 
+# Make it so that all users have full access
+TestApp::Model::User->add_trigger( before_access => sub { 'allow' } );
+
 # Create two users
 my $o = TestApp::Model::User->new(current_user => $system_user);
 $o->create( name => 'A User', email => 'auser at example.com', 

Modified: jifty/branches/virtual-models/t/TestApp/t/15-template-subclass.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/15-template-subclass.t	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/15-template-subclass.t	Tue Jun 12 15:48:07 2007
@@ -72,14 +72,8 @@
 my $mech = Jifty::Test::WWW::Mechanize->new;
 foreach my $test (@tests) {
     $mech->get_ok( $URL . $test->{url}, "get '$URL: /jifty/jifty/trunk/t/TestApp/t/15-template-subclass.t $test->{url}'" );
-    TODO: { 
-
-    local $TODO = " Template subclassing needs some love";
-    ok($mech->content =~ qr{$test->{text}}, "found the test content");
-    # $mech->content_contains breaks on multiline content
-    #$mech->content_contains( $test->{text}, "found content '$test->{text}'" );
-    };
 
+    $mech->content_contains( $test->{text}, "found content '$test->{text}'" );
 }
 
 1;

Added: jifty/branches/virtual-models/t/TestApp/t/16-template-region.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp/t/16-template-region.t	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,41 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use Jifty::Test;
+use Jifty::Test::WWW::Mechanize;
+
+my @tests = (
+    {   url  => "/foo/list",
+        text => q|list!
+<span>1</span>
+<span>2</span><script type="text/javascript">
+new Region('special',{'id':3},'/foo/item',null);
+</script><div id="region-special">
+<span>3</span></div>|
+    },
+
+);
+
+plan tests => 2 + scalar(@tests) * 2;
+
+my $server = Jifty::Test->make_server;
+isa_ok( $server, 'Jifty::Server' );
+my $URL = $server->started_ok;
+
+use Test::LongString;
+
+my $mech = Jifty::Test::WWW::Mechanize->new;
+foreach my $test (@tests) {
+    $mech->get_ok(
+        $URL . $test->{url},
+        "get '$URL: /jifty/jifty/trunk/t/TestApp/t/15-template-subclass.t $test->{url}'"
+    );
+    is_string( $mech->content, $test->{text}, "found the test content" );
+
+}
+
+1;

Added: jifty/branches/virtual-models/t/TestApp/t/17-template-region-internal-redirect.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp/t/17-template-region-internal-redirect.t	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use Jifty::Test;
+use Jifty::Test::WWW::Mechanize;
+
+plan tests => 7;
+
+
+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."/region-with-internal-redirect");
+$mech->content_like(qr'redirected ok');
+$mech->content_like(qr'other region');
+$mech->content_like(qr'still going');
+$mech->content_unlike(qr'sorry');
+
+
+1;

Added: jifty/branches/virtual-models/t/TestApp/t/before_access.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp/t/before_access.t	Tue Jun 12 15:48:07 2007
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use Jifty::Test tests => 4;
+
+use_ok('TestApp::Model::User');
+
+# Create a superuser, which will be denied access in a bit
+my $system_user = TestApp::CurrentUser->superuser;
+ok($system_user, 'Found a system user');
+
+# Associate a new rule that only names starting with bob can be created
+TestApp::Model::User->add_trigger( before_access => sub {
+    my ($self, $right, %args) = @_;
+    return 'ignore' unless $right eq 'create';
+    unless ($args{name} =~ /^bob/) {
+        return 'deny';
+    }
+    return 'ignore';
+});
+
+# Try creating non-bob, which will be denied
+my $o = TestApp::Model::User->new(current_user => $system_user);
+my ($id) = $o->create( name => 'nonbob', email => $$, password => $$ );
+ok(!$id, 'User could not be created');
+
+# Try creating bobette, which will be allowd
+($id) = $o->create( name => 'bobette', email => $$, password => $$ );
+ok($id, 'User could be created');

Modified: jifty/branches/virtual-models/t/lib/Jifty/SubTest.pm
==============================================================================
--- jifty/branches/virtual-models/t/lib/Jifty/SubTest.pm	(original)
+++ jifty/branches/virtual-models/t/lib/Jifty/SubTest.pm	Tue Jun 12 15:48:07 2007
@@ -4,6 +4,7 @@
 use File::Spec;
 BEGIN {
     @INC = grep { defined } map { ref($_) ? $_ : File::Spec->rel2abs($_) } @INC;
+    $0 = File::Spec->rel2abs($0);
     chdir "$FindBin::Bin/..";
 }
 


More information about the Jifty-commit mailing list