[Jifty-commit] r4428 - in jifty/branches/virtual-models: . bin lib/Jifty lib/Jifty/Action lib/Jifty/Param lib/Jifty/Plugin lib/Jifty/Plugin/AutoReference lib/Jifty/Plugin/Feedback/Action lib/Jifty/Plugin/OAuth lib/Jifty/Plugin/OAuth/Action lib/Jifty/Plugin/OAuth/Model lib/Jifty/Test/WWW lib/Jifty/View/Declare lib/Jifty/Web lib/Jifty/Web/Form t t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth t/TestApp-Plugin-OAuth/t t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage t/TestApp/t

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Tue Nov 13 14:05:22 EST 2007


Author: sterling
Date: Tue Nov 13 14:05:20 2007
New Revision: 4428

Added:
   jifty/branches/virtual-models/bin/sort-changelog   (contents, props changed)
   jifty/branches/virtual-models/contrib/
   jifty/branches/virtual-models/contrib/jifty_completion.sh
   jifty/branches/virtual-models/lib/Jifty/Manual.pm
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/View.pm
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/04-access-token.t
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/05-protected-resource.t
Modified:
   jifty/branches/virtual-models/   (props changed)
   jifty/branches/virtual-models/AUTHORS
   jifty/branches/virtual-models/Makefile.PL
   jifty/branches/virtual-models/bin/generate-changelog
   jifty/branches/virtual-models/lib/Jifty/Action/Record.pm
   jifty/branches/virtual-models/lib/Jifty/Collection.pm
   jifty/branches/virtual-models/lib/Jifty/DateTime.pm
   jifty/branches/virtual-models/lib/Jifty/Handle.pm
   jifty/branches/virtual-models/lib/Jifty/I18N.pm
   jifty/branches/virtual-models/lib/Jifty/Param/Schema.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/AutoReference/Widget.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/I18N.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Dispatcher.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/View.pm
   jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm
   jifty/branches/virtual-models/lib/Jifty/Record.pm
   jifty/branches/virtual-models/lib/Jifty/Test.pm
   jifty/branches/virtual-models/lib/Jifty/Test/WWW/Mechanize.pm
   jifty/branches/virtual-models/lib/Jifty/Test/WWW/Selenium.pm
   jifty/branches/virtual-models/lib/Jifty/View/Declare/Handler.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/t/99-pod-coverage.t
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/00-test-setup.t
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/01-basic.t
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/02-request-token.t
   jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/03-authorize.t
   jifty/branches/virtual-models/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm
   jifty/branches/virtual-models/t/TestApp/t/11-current_user.t
   jifty/branches/virtual-models/t/TestApp/t/18-test-www-declare.t

Log:
 r14041 at dynpc145:  andrew | 2007-11-13 13:04:39 -0600
 Merge down from trunk.
  r13805 at dynpc145:  andrew | 2007-10-31 15:42:55 -0500
   r13804 at riddle (orig r4349):  sunnavy | 2007-10-31 14:20:42 -0500
   we need DateTime::Locale
  
  r13817 at dynpc145:  andrew | 2007-11-02 14:58:30 -0500
  Fixed a problem where AutoReference breaks in Search actions.
  r13836 at dynpc145:  andrew | 2007-11-02 15:18:52 -0500
   r13818 at riddle (orig r4350):  sartak | 2007-10-31 16:15:18 -0500
    r44469 at onn:  sartak | 2007-10-31 17:14:59 -0400
    Most AccessToken tests done :) first implementation was right on
   
   r13819 at riddle (orig r4351):  sartak | 2007-10-31 16:15:50 -0500
    r44473 at onn:  sartak | 2007-10-31 17:15:32 -0400
    Whoops, committed in a subdirectory
   
   r13820 at riddle (orig r4352):  sartak | 2007-10-31 17:07:41 -0500
    r44475 at onn:  sartak | 2007-10-31 18:07:21 -0400
    Various refactors and cleanups
    Request tokens are now properly set to "used"
    Life of a request token lengthened on authorize
    Fixed duplicate timestamp/nonce check (and test)
   
   r13821 at riddle (orig r4353):  audreyt | 2007-11-01 12:13:52 -0500
   * Jifty::I18N::promote_encoding - r4286 by yours truly broke 
     auto-decoding for POST requests.
     
     This is a better fix to avoid the "Unquoted / not allowed in
     Content-Type" warning (which arugably is a Email::MIME::ContentType
     glitch), by scanning the Content-Type header for the substring
     "charset" before parsing it.
   
     This way, when "charset" is not found in that header,
     we still fallback to Encode::Guess, regardless of whether
     the request was of type "multipart/form-data".
   r13822 at riddle (orig r4354):  alexmv | 2007-11-01 13:42:40 -0500
    r24221 at zoq-fot-pik:  chmrr | 2007-10-30 18:21:04 -0400
     * Work around a "feature" in Hash::Merge < 0.10
   
   r13823 at riddle (orig r4355):  alexmv | 2007-11-01 13:43:37 -0500
   
   r13824 at riddle (orig r4356):  alexmv | 2007-11-01 13:43:47 -0500
   
   r13825 at riddle (orig r4357):  alexmv | 2007-11-01 13:43:51 -0500
    r24273 at zoq-fot-pik:  chmrr | 2007-11-01 14:42:20 -0400
     * Horrible Hash::Merge workaround, part 2
   
   r13827 at riddle (orig r4359):  sartak | 2007-11-02 13:43:38 -0500
    r44548 at onn:  sartak | 2007-11-02 14:43:04 -0400
    Correctly skip OAuth tests in the absence of Net::OAuth
   
   r13828 at riddle (orig r4360):  sartak | 2007-11-02 13:48:06 -0500
    r44550 at onn:  sartak | 2007-11-02 14:47:40 -0400
    Make Test::WWW::Declare an optional dep for now
   
  
  r14040 at dynpc145:  andrew | 2007-11-13 13:02:50 -0600
   r13982 at dynpc145 (orig r4370):  clkao | 2007-11-04 01:15:12 -0600
   Add an additional trigger in Clickable for SinglePage to filter
   out state variables for region-__page.
   
   r13983 at dynpc145 (orig r4371):  clkao | 2007-11-04 05:23:36 -0600
   I18N plugin:
   - log when user is somehow able to pass in disallowed lang.
   - inline the initial dictionary json.
   
   r13988 at dynpc145 (orig r4376):  sartak | 2007-11-05 19:09:00 -0600
    r44469 at onn:  sartak | 2007-10-31 17:14:59 -0400
    Most AccessToken tests done :) first implementation was right on
   
   r13989 at dynpc145 (orig r4377):  sartak | 2007-11-05 19:09:12 -0600
    r44473 at onn:  sartak | 2007-10-31 17:15:32 -0400
    Whoops, committed in a subdirectory
   
   r13990 at dynpc145 (orig r4378):  sartak | 2007-11-05 19:09:23 -0600
    r44475 at onn:  sartak | 2007-10-31 18:07:21 -0400
    Various refactors and cleanups
    Request tokens are now properly set to "used"
    Life of a request token lengthened on authorize
    Fixed duplicate timestamp/nonce check (and test)
   
   r13991 at dynpc145 (orig r4379):  sartak | 2007-11-05 19:09:50 -0600
    r44697 at onn:  sartak | 2007-11-05 20:08:14 -0500
    Add a Jifty::DateTime set_current_user_timezone method
   
   r13992 at dynpc145 (orig r4380):  sartak | 2007-11-05 19:25:16 -0600
    r44707 at onn:  sartak | 2007-11-05 20:24:45 -0500
    Add Jifty::DateTime::from_epoch which does our usual timezone magic, use set_current_user_timezone more, too
   
   r13993 at dynpc145 (orig r4381):  clkao | 2007-11-06 02:11:39 -0600
   Add a ForwardCompatible config in Database section.  The usecase
   is that when you switch your checkout to something with older
   version of app schema, you can declare it is compatiable with
   the future version that is currently in db.
   
   r13994 at dynpc145 (orig r4382):  clkao | 2007-11-06 02:29:55 -0600
   support SELENIUM_RC_TEST_AGAINST and SELENIUM_RC_BROWSER environment variable, and allow args to be passed into selenium rc invoker.
   r13995 at dynpc145 (orig r4383):  alexmv | 2007-11-06 12:41:25 -0600
    r24425 at zoq-fot-pik:  chmrr | 2007-11-06 13:37:43 -0500
     * Remove debugging statement
   
   r13996 at dynpc145 (orig r4384):  clkao | 2007-11-06 16:23:51 -0600
   I18n: fix json dictionary fallback.
   r13997 at dynpc145 (orig r4385):  clkao | 2007-11-06 16:28:02 -0600
   I18N: load json dict from absolute path.
   r13998 at dynpc145 (orig r4386):  sartak | 2007-11-06 20:45:50 -0600
    r44749 at onn:  sartak | 2007-11-06 21:45:14 -0500
    First cut of protected resource requests
   
   r14003 at dynpc145 (orig r4391):  jesse | 2007-11-07 11:47:02 -0600
    r71485 at pinglin:  jesse | 2007-11-07 12:46:14 -0500
    * only run pod coverage tests if you're a developer
   
   r14007 at dynpc145 (orig r4395):  sartak | 2007-11-09 12:31:35 -0600
    r44778 at onn:  sartak | 2007-11-07 14:41:01 -0500
    Refactor the tests
   
   r14008 at dynpc145 (orig r4396):  sartak | 2007-11-09 12:31:43 -0600
    r44779 at onn:  sartak | 2007-11-07 14:50:59 -0500
    Whoops, the consumer and user were using the same mech. But fixing that didn't break anything :)
   
   r14009 at dynpc145 (orig r4397):  sartak | 2007-11-09 12:31:49 -0600
    r44785 at onn:  sartak | 2007-11-07 15:36:47 -0500
    Begin adding tests for protected resource requests
   
   r14010 at dynpc145 (orig r4398):  sartak | 2007-11-09 12:31:54 -0600
    r44786 at onn:  sartak | 2007-11-07 15:37:39 -0500
    Various improvements of try_oauth so it gets further (but not all the way) in the correct codepath
   
   r14011 at dynpc145 (orig r4399):  sartak | 2007-11-09 12:32:06 -0600
    r44787 at onn:  sartak | 2007-11-07 15:49:58 -0500
    More fixes to get protected resource requests going
    We might need a WWW::Mechanize::OAuth subclass, so that each get/post will be properly wrapped as an OAuth request
   
   r14017 at dynpc145 (orig r4405):  sartak | 2007-11-09 19:56:11 -0600
    r44970 at onn:  sartak | 2007-11-09 20:47:18 -0500
    Add another changelog sorting program, this one I think works a bit better :)
   
   r14018 at dynpc145 (orig r4406):  sartak | 2007-11-09 19:56:24 -0600
    r44971 at onn:  sartak | 2007-11-09 20:47:42 -0500
    Sort change groups by header, typo fix
   
   r14019 at dynpc145 (orig r4407):  sartak | 2007-11-09 19:56:27 -0600
    r44972 at onn:  sartak | 2007-11-09 20:55:15 -0500
    Add 'performance' tag to sort-changelog
   
   r14020 at dynpc145 (orig r4408):  sartak | 2007-11-09 20:08:31 -0600
    r44981 at onn:  sartak | 2007-11-09 21:08:04 -0500
    Add a Jifty::Manual which lists each of the manuals with a short description
    Secret confession: I was tired of perldoc Jifty::Manual::<tab> failing :)
   
   r14021 at dynpc145 (orig r4409):  gugod | 2007-11-10 02:04:39 -0600
   When it's a ::Create action, $event_info won't have a record_id
   there, cause a fatal error when it's published latter.
   
   r14022 at dynpc145 (orig r4410):  sunnavy | 2007-11-10 04:04:05 -0600
   refactor Jifty::Test a bit
   r14026 at dynpc145 (orig r4414):  clkao | 2007-11-10 10:33:08 -0600
   older Email::LocalDelivery is buggy.
   r14027 at dynpc145 (orig r4415):  jesse | 2007-11-10 23:47:37 -0600
    r71589 at pinglin:  jesse | 2007-11-09 12:51:51 -0500
    * Get rid of another place where we had a custom accessor that meant 'arguments' - for great consistency
   
   r14028 at dynpc145 (orig r4416):  jesse | 2007-11-10 23:47:57 -0600
    r71590 at pinglin:  jesse | 2007-11-09 12:52:30 -0500
    * Upgrade a region error from a debug to a warning. 
   
   r14029 at dynpc145 (orig r4417):  jesse | 2007-11-10 23:48:05 -0600
    r71639 at pinglin:  jesse | 2007-11-11 00:46:59 -0500
    * Fix compatibility with Test::WWW::Mechanize
   
   r14030 at dynpc145 (orig r4418):  c9s | 2007-11-11 23:58:25 -0600
   A command line completion script is added into contrib/
   r14033 at dynpc145 (orig r4421):  clkao | 2007-11-12 12:17:49 -0600
   In TD View handler, don't just return without printing
   headers if the response content is empty, as when you
   access /__jifty/empty.
   
   r14036 at dynpc145 (orig r4424):  chapman | 2007-11-13 10:53:51 -0600
   Fixed the feedback plugin so it works even if the current
   user doesn't have permission to read their own email.
   
   r14039 at dynpc145 (orig r4427):  alexmv | 2007-11-13 12:37:29 -0600
    r24696 at zoq-fot-pik:  chmrr | 2007-11-13 13:36:31 -0500
     * POD re-wrapping
     * perltidy
     * Use Jifty::DBI's new ->_new_record_args and _new_collection_args
     * Bump the JDBI dep accordingly
   
  
 


Modified: jifty/branches/virtual-models/AUTHORS
==============================================================================
--- jifty/branches/virtual-models/AUTHORS	(original)
+++ jifty/branches/virtual-models/AUTHORS	Tue Nov 13 14:05:20 2007
@@ -32,3 +32,5 @@
 sunnavy <sunnavy at gmail.com>
 Shawn M Moore <sartak at gmail.com>
 Edward Funnekotter <efunneko at gmail.com>
+Cornelius Lin <c9s at aiink.com>
+Todd Chapman <todd at chaka.net>

Modified: jifty/branches/virtual-models/Makefile.PL
==============================================================================
--- jifty/branches/virtual-models/Makefile.PL	(original)
+++ jifty/branches/virtual-models/Makefile.PL	Tue Nov 13 14:05:20 2007
@@ -20,9 +20,10 @@
 requires('Data::Page');
 requires('Data::UUID');
 requires('DateTime');
+requires('DateTime::Locale');
 requires('Date::Manip');
 requires('Email::Folder');
-requires('Email::LocalDelivery');
+requires('Email::LocalDelivery' => 0.217 );
 requires('Email::MIME');
 requires('Email::MIME::Creator');
 requires('Email::MIME::ContentType');
@@ -49,7 +50,7 @@
 requires('Hook::LexWrap');
 requires('IPC::PubSub' => '0.23' );
 requires('IPC::Run3');
-requires('Jifty::DBI' => '0.45' );            # Jifty::DBI::Collection Jifty::DBI::Handle Jifty::DBI::Record::Cachable Jifty::DBI::SchemaGenerator
+requires('Jifty::DBI' => '0.47' );            # 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');
@@ -77,7 +78,6 @@
 requires('Test::LongString');
 requires('Test::More' => 0.62 ),
 requires('Test::Pod::Coverage'),
-requires('Test::WWW::Declare'),
 requires('Test::WWW::Mechanize' => 1.04 ),
 requires('Test::WWW::Selenium'),
 requires('WWW::Mechanize' => 1.30 ),
@@ -116,6 +116,7 @@
         recommends('Test::MockModule' => '0.05'),
         recommends('Test::MockObject' => '1.07'),
         recommends('Module::Refresh' => '0.09'),
+        recommends('Test::WWW::Declare' => '0.01'),
     ],
     'Development of the jifty framework' => [
         -default => 0,

Modified: jifty/branches/virtual-models/bin/generate-changelog
==============================================================================
--- jifty/branches/virtual-models/bin/generate-changelog	(original)
+++ jifty/branches/virtual-models/bin/generate-changelog	Tue Nov 13 14:05:20 2007
@@ -24,7 +24,7 @@
 use Term::ReadKey;
 use Data::Dumper;
          use File::Temp qw/ tempfile tempdir /;
-my @tags = qw(doc install core plugin security view f-bug t-discard backward-compatiblity-problem u-pubsub r-crud e-testing);
+my @tags = qw(doc install core plugin security view f-bug t-discard backward-compatibility-problem u-pubsub r-crud e-testing);
 
 my %tags = map { substr($_,0,1) => $_ } @tags;
 my $mode = shift @ARGV;
@@ -81,7 +81,7 @@
 
     open (my $fh, ">$dest") or die "Can't open $dest for writing";
 
-    foreach my $key ( keys %entries) {
+    foreach my $key (sort keys %entries) {
     my $title = $key;
     $title =~ s/^\w\-//;
         print $fh uc($key)."\n";

Added: jifty/branches/virtual-models/bin/sort-changelog
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/bin/sort-changelog	Tue Nov 13 14:05:20 2007
@@ -0,0 +1,204 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use File::Slurp;
+use XML::Simple;
+use Term::ReadKey;
+use Term::CallEditor;
+use Term::ANSIScreen;
+use Text::Autoformat;
+
+my %commands = (
+    j   => sub { ++$_ },
+    k   => sub { --$_ },
+    q   => sub { die "Quitting.\n" },
+    e   => \&edit_entry,
+    t   => \&next_tag,
+);
+
+my %tags = (
+    D => 'doc',
+    I => 'install',
+    C => 'core',
+    P => 'plugin',
+    S => 'security',
+    T => 'testing',
+    V => 'view',
+    F => 'bugfix',
+    X => 'discard',
+    B => 'backward-compatibility-problem',
+    U => 'pubsub',
+    R => 'crud',
+    8 => 'I18N',
+    M => 'misc',
+    Q => 'performance',
+);
+
+my $command_string = join '', sort keys %commands;
+my $tag_string     = join '', sort keys %tags;
+my $untagged;
+
+# each tag gets its own command
+while (my ($k, $v) = each %tags) {
+    $commands{$k} = sub {
+        --$untagged if ($_[0]->{section}||'') eq '';  # update untagged count
+        $_[0]->{section} = $v;                        # apply tag
+        ++$_                                          # advance cursor
+    };
+}
+
+# other commands
+$commands{' '} = $commands{j};
+$commands{'?'} = sub {
+    print << "HELP";
+j - next entry
+k - previous entry
+q - write and quit
+e - edit entry
+t - find next entry with a different tag
+
+HELP
+    for (sort keys %tags) {
+        print "$_ - tag as $tags{$_}, next entry\n";
+    }
+
+    print "\nPress any key to continue.\n";
+    key();
+};
+
+# get the files
+ at ARGV == 2
+    or die "Usage: $0 in.xml out.yml";
+
+my $in = shift;
+my $out = shift;
+
+$| = 1;
+
+# read data
+my $data = XMLin(read_file($in)."");
+
+my @entries =
+              # sort by tag
+              sort { ($a->{section}||'') cmp ($b->{section}||'') }
+
+              # help find duplicate messages
+              sort { $a->{msg} cmp $b->{msg} }
+
+              # clean up incoming messages
+              map { $_->{msg} = reformat_message($_->{msg}); $_ }
+
+              # remove no-msg commits
+              grep { !ref($_->{msg}) }
+
+              @{$data->{logentry} || $data->{opt}};
+
+# count untagged entries
+$untagged = grep { ($_->{section}||'') eq '' } @entries;
+
+munge_entries();
+
+# make sure we always print our output out
+END { write_entries() }
+
+sub write_entries {
+    $data->{logentry} = \@entries;
+    open my $outfh, '>', $out
+        or die "Unable to open $out for writing: $!";
+    print $outfh XMLout($data, NoAttr => 1);
+    close $outfh;
+}
+
+sub munge_entries {
+    $_ = 0;
+
+    while (1) {
+        display($entries[$_]);
+
+        # ask the user what to do
+        print "Now what? [$command_string] [$tag_string] or ?: ";
+        my $c = key();
+
+        unless (exists $commands{$c}) {
+            warn "Invalid command '$c'. Press a key to continue.\n";
+            key();
+            next;
+        }
+
+        $commands{$c}->( $entries[$_] );
+
+        if ($_ < 0) {
+            $_ += @entries;
+        }
+        if ($_ >= @entries) {
+            $_ -= @entries;
+        }
+    }
+}
+
+sub display {
+    my $entry = shift;
+
+    my $term = Term::ANSIScreen->new;
+    $term->Cls();
+    $term->Cursor(0, 0);
+
+    print "Number:    $_/$#entries  ($untagged untagged)\n";
+    print "Author:    $entry->{author}\n";
+    print "Date:      $entry->{date}\n";
+    print "Tagged as: ", $entry->{section} || '(none)', "\n";
+    print "-" x 79, "\n";
+    print $entry->{msg}, "\n\n";
+}
+
+sub reformat_message {
+    my $msg = shift;
+
+    # try to kill svn header
+    $msg =~ s/^\s*r\d+\@\S+:\s*\S+\s*\|\s*.*\n//;
+
+    # autoformat
+    $msg = autoformat $msg;
+
+    # remove leading whitespace
+    $msg =~ s/(\n|^)\s+/$1/g;
+
+    # chomp off all newlines
+    1 while chomp $msg;
+
+    return $msg;
+}
+
+sub edit_entry {
+    my $entry = shift;
+    my ($fh, $fn) = solicit($entry->{msg});
+    close $fh;
+    $entry->{msg} = do { local (@ARGV, $/) = $fn; <> };
+    $entry->{msg} = reformat_message($entry->{msg});
+}
+
+sub next_tag {
+    my $start = $_;
+    my $section = $_[0]->{section};
+
+    # wrap in eval just in case this logic screws up, the user can smash ^C and
+    # not kill everything
+    eval {
+        local $SIG{INT} = sub { die };
+        while (1) {
+            ++$_;
+            $_ -= @entries if $_ >= @entries;
+            last if $start == $_;
+            last if $entries[$_]->{section} ne $section;
+        }
+    }
+}
+
+sub key {
+    ReadMode 3;
+    my $c = ReadKey 0;
+    ReadMode 0;
+    print "$c\n";
+    return $c;
+}
+

Added: jifty/branches/virtual-models/contrib/jifty_completion.sh
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/contrib/jifty_completion.sh	Tue Nov 13 14:05:20 2007
@@ -0,0 +1,55 @@
+#-*- mode: shell-script;-*-
+#
+# bash complition script for jifty 
+# put this file into /etc/bash_completion.d/
+
+have jifty &&
+_jifty()
+{
+    local cur
+    cur=${COMP_WORDS[COMP_CWORD]}
+
+    local jifty_commands="action adopt app console deps env fastcgi help modperl2 model plugin po schema server"
+    if (($COMP_CWORD == 1)); then
+        COMPREPLY=( $( compgen -W "$jifty_commands" -- $cur ) )
+        return 0
+    fi
+
+    local opts_schema="--create-database --drop-database --help --ignore-reserved-words --man --print --setup"
+    local opts_server="--port"
+    local opts_action="--name --force"
+    local opts_model="--name --force"
+    local opts_op="--js --dir --language"
+    local opts_adopt="--ls"
+
+    case "${COMP_WORDS[1]}" in 
+        schema)
+            COMPREPLY=( $( compgen -W "$opts_schema" -- $cur ) )
+            return 0
+        ;;
+        server)
+            COMPREPLY=( $( compgen -W "$opts_server" -- $cur ) )
+            return 0
+        ;;
+        action)
+            COMPREPLY=( $( compgen -W "$opts_action" -- $cur ) )
+            return 0
+        ;;
+        model)
+            COMPREPLY=( $( compgen -W "$opts_model" -- $cur ) )
+            return 0
+        ;;
+        op)
+            COMPREPLY=( $( compgen -W "$opts_op" -- $cur ) )
+            return 0
+        ;;
+        adopt)
+            COMPREPLY=( $( compgen -W "$opts_adopt" -- $cur ) )
+            return 0
+        ;;
+        *)
+        ;;
+    esac
+}
+
+[ "$have" ] && complete -F _jifty -o default jifty

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 Nov 13 14:05:20 2007
@@ -478,6 +478,12 @@
     my $self = shift;
     my $event_info = shift;
 
+    unless (defined $event_info->{record_id}) {
+        $event_info->{record_id} = $self->record->id;
+        $event_info->{record_class} = ref($self->record);
+        $event_info->{action_class} = ref($self);
+    }
+
     # Add a few more bits about the result
     $event_info->{result} = $self->result;    
     $event_info->{timestamp} = time(); 

Modified: jifty/branches/virtual-models/lib/Jifty/Collection.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Collection.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Collection.pm	Tue Nov 13 14:05:20 2007
@@ -39,15 +39,16 @@
 
 Returns a L<Data::Page> object associated with this collection.  This
 object defaults to 10 entries per page.  You should use only use
-L<Data::Page>  methods on this object to B<get> information about paging,
-not to B<set> it; use C<set_page_info> to set paging information.
+L<Data::Page> methods on this object to B<get> information about
+paging, not to B<set> it; use C<set_page_info> to set paging
+information.
 
 =head2 results_are_readable
 
 If your results from the query are guaranteed to be readable by
 current_user, you can create the collection with
-C<results_are_readable => 1>.  This causes check_read_rights to
-bypass normal current_user_can checks.
+C<results_are_readable => 1>.  This causes check_read_rights to bypass
+normal current_user_can checks.
 
 =cut
 
@@ -55,9 +56,11 @@
 
 =head2 as_search_action PARAMHASH
 
-Returns the L<Jifty::Action::Record::Search> action for the model associated with this collection.
+Returns the L<Jifty::Action::Record::Search> action for the model
+associated with this collection.
 
-The PARAMHASH allows you to add additional parameters to pass to L<Jifty::Web/new_action>.
+The PARAMHASH allows you to add additional parameters to pass to
+L<Jifty::Web/new_action>.
 
 =cut
 
@@ -68,15 +71,16 @@
 
 =head2 add_record
 
-If L</results_are_readable> is false, only add records to the collection that
-we can read (by checking L<Jifty::Record/check_read_rights>). Otherwise, make
-sure all records added are readable.
+If L</results_are_readable> is false, only add records to the
+collection that we can read (by checking
+L<Jifty::Record/check_read_rights>). Otherwise, make sure all records
+added are readable.
 
 =cut
 
 sub add_record {
     my $self = shift;
-    my($record) = (@_);
+    my ($record) = (@_);
 
     # If results_are_readable is set, guarantee that they are
     $record->_is_readable(1)
@@ -98,24 +102,24 @@
 sub _init {
     my $self = shift;
     my %args = (
-        record_class => undef,
-        current_user => undef,
+        record_class         => undef,
+        current_user         => undef,
         results_are_readable => undef,
         @_
     );
 
     # Setup the current user, record class, results_are_readable
     $self->_get_current_user(%args);
-    $self->record_class($args{record_class}) if defined $args{record_class};
-    $self->results_are_readable($args{results_are_readable});
+    $self->record_class( $args{record_class} ) if defined $args{record_class};
+    $self->results_are_readable( $args{results_are_readable} );
 
     # Bad stuff, we really need one of these
-    unless ($self->current_user) {
+    unless ( $self->current_user ) {
         Carp::confess("Collection created without a current user");
     }
 
     # Setup the table and call the super-implementation
-    $self->table($self->new_item->table());
+    $self->table( $self->new_item->table() );
     $self->SUPER::_init(%args);
 }
 
@@ -127,23 +131,17 @@
 
 sub implicit_clauses {
     my $self = shift;
-    $self->order_by( column => 'id',order => 'asc');
+    $self->order_by( column => 'id', order => 'asc' );
 }
 
-=head2 new_item
-
-Overrides L<Jifty::DBI::Collection>'s new_item to pass in the current
-user.
-
-=cut
-
-sub new_item {
+sub _new_record_args {
     my $self = shift;
-    my $class =$self->record_class();
+    return ( current_user => $self->current_user );
+}
 
-    # We do this as a performance optimization, so we don't need to do the stackwalking to find it
-    Jifty::Util->require($class);
-    return $class->new(current_user => $self->current_user);
+sub _new_collection_args {
+    my $self = shift;
+    return ( current_user => $self->current_user );
 }
 
 =head1 SEE ALSO

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 Nov 13 14:05:20 2007
@@ -69,7 +69,10 @@
         # we want to convert to the end-user's timezone. If we ignore
         # $args{time_zone}, then DateTime::from_epoch will get very confused
         if (!$args{time_zone} and my $tz = $self->current_user_has_timezone) {
-            $self->set_time_zone("UTC"); # XXX: why are we doing this?
+
+            # XXX: we do this because of the floating timezone
+            $self->set_time_zone("UTC");
+
             $self->set_time_zone( $tz );
         }
     }
@@ -100,7 +103,24 @@
     my %args  = @_;
     my $self  = $class->SUPER::now(%args);
 
-    $self->set_time_zone($self->current_user_has_timezone || 'UTC')
+    $self->set_current_user_timezone()
+        unless $args{time_zone};
+
+    return $self;
+}
+
+=head2 from_epoch ARGS
+
+See L<DateTime/from_epoch> and L<Jifty::DateTime/now>.
+
+=cut
+
+sub from_epoch {
+    my $class = shift;
+    my %args  = @_;
+    my $self  = $class->SUPER::from_epoch(%args);
+
+    $self->set_current_user_timezone()
         unless $args{time_zone};
 
     return $self;
@@ -117,7 +137,7 @@
     $self->_get_current_user();
 
     # Can't continue if we have no notion of a user_object
-    return unless $self->current_user->can('user_object');
+    $self->current_user->can('user_object') or return;
 
     # Can't continue unless the user object is defined
     my $user_obj = $self->current_user->user_object or return;
@@ -127,6 +147,23 @@
     return $f->($user_obj);
 }
 
+=head2 set_current_user_timezone [DEFAULT_TZ]
+
+Set this Jifty::DateTime's timezone to the current user's timezone. If that's
+not available, then use the passed in DEFAULT_TZ (or GMT if not passed in).
+Returns the Jifty::DateTime object itself.
+
+=cut
+
+sub set_current_user_timezone {
+    my $self    = shift;
+    my $default = shift || 'GMT';
+    my $tz = $self->current_user_has_timezone || $default;
+
+    $self->set_time_zone($tz);
+    return $self;
+}
+
 =head2 new_from_string STRING
 
 Take some user defined string like "tomorrow" and turn it into a

Modified: jifty/branches/virtual-models/lib/Jifty/Handle.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Handle.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Handle.pm	Tue Nov 13 14:05:20 2007
@@ -98,7 +98,7 @@
 
     my %lc_db_config;
     # Skip the non-dsn keys, but not anything else
-    for (grep {!/^checkschema|version|recordbaseclass|attributes$/i} keys %db_config) {
+    for (grep {!/^checkschema|version|forwardcompatible|recordbaseclass|attributes$/i} keys %db_config) {
         $lc_db_config{lc($_)} = $db_config{$_};
     }
     $self->SUPER::connect( %lc_db_config , %args);
@@ -152,10 +152,14 @@
             . "\t bin/jifty schema --setup\n"
             unless defined $dbv;
 
-        die
+        unless (version->new($appv) == version->new($dbv)) {
+            # if app version is older than db version, but we are still compatible
+            my $compat = delete Jifty->config->framework('Database')->{'ForwardCompatible'} || $appv;
+            die
             "Application schema version in database ($dbv) doesn't match application schema version ($appv)\n"
             . "Please run `bin/jifty schema --setup` to upgrade the database.\n"
-            unless version->new($appv) == version->new($dbv);
+                if version->new($appv) > version->new($dbv) || version->new($compat) < version->new($dbv);
+        }
     }
 
     # Jifty db version check

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 Nov 13 14:05:20 2007
@@ -230,12 +230,10 @@
     my $content_type = shift;
     my $charset;
 
-    if ($content_type) {
-        # Multi-part form data have no notion of charsets, so we return the string
-        # verbatim here, to avoid the "Unquoted / not allowed in Content-Type"
-        # warnings when the Base64-encoded MIME boundary string contains "/".
-        return $string if $content_type =~ /^multipart\b/;
-
+    # Don't bother parsing the Content-Type header unless it mentions "charset".
+    # This is to avoid the "Unquoted / not allowed in Content-Type" warnings when
+    # the Base64-encoded MIME boundary string contains "/".
+    if ($content_type and $content_type =~ /charset/i) {
         $content_type = Email::MIME::ContentType::parse_content_type($content_type);
         $charset = $content_type->{attributes}->{charset};
     }

Added: jifty/branches/virtual-models/lib/Jifty/Manual.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/lib/Jifty/Manual.pm	Tue Nov 13 14:05:20 2007
@@ -0,0 +1,89 @@
+=head1 NAME
+
+Jifty::Manual - Jifty documentation
+
+=head1 DESCRIPTION
+
+This manual is divided into many separate pages, each covering a specific topic
+thoroughly.
+
+=over 4
+
+=item L<Jifty::Manual::AccessControl>
+
+Implementing access controls in your Jifty app
+
+=item L<Jifty::Manual::Actions>
+
+All about actions and how to use them
+
+=item L<Jifty::Manual::Continuations>
+
+Continuations made easy. Easier..
+
+=item L<Jifty::Manual::Cookbook>
+
+Chunk-sized pearls of wisdom for accomplishing common tasks
+
+=item L<Jifty::Manual::Deploying>
+
+How to go from C<jifty server> to a robust web-server and database environment
+
+=item L<Jifty::Manual::FAQ>
+
+Frequently asked questions, for some value of frequent
+
+=item L<Jifty::Manual::Glossary>
+
+Common terms that you might want to learn
+
+=item L<Jifty::Manual::Models>
+
+All about models -- the creepy crawlies that live in your database
+
+=item L<Jifty::Manual::ObjectModel>
+
+How all the bits of Jifty hang together
+
+=item L<Jifty::Manual::PageRegions>
+
+You too can have sexy AJAX!
+
+=item L<Jifty::Manual::RequestHandling>
+
+The life cycle of a page load
+
+=item L<Jifty::Manual::Style>
+
+Style guide for the Jifty project itself
+
+=item L<Jifty::Manual::Tutorial>
+
+Your first stop on this tour
+
+=item L<Jifty::Manual::TutorialRest>
+
+Using the REST API in your application
+
+=item L<Jifty::Manual::Tutorial_de>
+
+The tutorial in German
+
+=item L<Jifty::Manual::Tutorial_ja>
+
+The tutorial in Japanese
+
+=item L<Jifty::Manual::Upgrading>
+
+How to change your application's database
+
+=item L<Jifty::Manual::UsingCSSandJS>
+
+How to use custom CSS and JavaScript in your application
+
+=back
+
+=cut
+
+1;
+

Modified: jifty/branches/virtual-models/lib/Jifty/Param/Schema.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Param/Schema.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Param/Schema.pm	Tue Nov 13 14:05:20 2007
@@ -172,18 +172,30 @@
 =cut
 
 sub merge_params {
-    my @merge = @_;
-
     # We pull this deref and re-ref trick to un-bless any
     # Jifty::Params which might exist; Hash::Merge pre-0.10 merged
-    # objects and hahrefs with no complaint, but 0.10 doesn't.
-    for my $m (@merge) {
-        $m->{$_} = {%{$m->{$_}}} for keys %{$m};
+    # objects and hahrefs with no complaint, but 0.10 doesn't.  This
+    # is a horrible, horrible hack, and will hopeflly be able to be
+    # backed out if and when Hash::Merge reverts to the old behavior.
+    my @types;
+    for my $m (@_) {
+        my @t;
+        for (keys %{$m}) {
+            push @t, ref $m->{$_};
+            bless $m->{$_}, "HASH";
+        }
+        push @types, \@t;
     }
     my $prev_behaviour = Hash::Merge::get_behavior();
     Hash::Merge::specify_behavior( MERGE_PARAM_BEHAVIOUR, "merge_params" );
-    my $rv = Hash::Merge::merge(@merge);
+    my $rv = Hash::Merge::merge(@_);
     Hash::Merge::set_behavior( $prev_behaviour );
+    for my $m (@_) {
+        my @t = @{shift @types};
+        for (keys %{$m}) {
+            bless $m->{$_}, shift @t;
+        }
+    }
     return $rv;
 }
 

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/AutoReference/Widget.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/AutoReference/Widget.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/AutoReference/Widget.pm	Tue Nov 13 14:05:20 2007
@@ -46,6 +46,10 @@
 
     my $name      = $self->name;
     my $column    = $self->action->record->column($name);
+
+    # XXX Does this need more advanced handling?
+    return unless $column; # happens in SearchXXX
+
     my $reference = $column->refers_to;
     my $brief     = $reference->can('_brief_description') ?
                         $reference->_brief_description : 'name';

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/Feedback/Action/SendFeedback.pm	Tue Nov 13 14:05:20 2007
@@ -58,13 +58,19 @@
     # 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
-    );
+
+    if (    Jifty->web->current_user->id
+         && Jifty->web->current_user->user_object->can('email') ) {
+
+         my $user = Jifty->web->current_user->user_object;
+         my $CurrentUser = Jifty->app_class('CurrentUser');
+         $user->current_user( $CurrentUser->superuser );
+         $mail->from( $user->email() || $plugin->from );
+    }
+    else {
+        $mail->from( $plugin->from );
+    }
+
     $mail->recipients( $plugin->to );
     $mail->subject( "["
             . Jifty->config->framework('ApplicationName')

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/I18N.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/I18N.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/I18N.pm	Tue Nov 13 14:05:20 2007
@@ -64,9 +64,42 @@
 sub _i18n_js {
     my $self = shift;
 
-    # js l10n init
     my $current_lang = Jifty::I18N->get_current_language || 'en';
-    Jifty->web->out(qq{<script type="text/javascript">Localization.init({dict_path: '/static/js/dict', lang: '$current_lang'});</script>});
+
+    # diagnosis for htf the client is requesting something not in allowed_lang.
+    my $allowed_lang = Jifty->config->framework('L10N')->{'AllowedLang'};
+    $allowed_lang = [defined $allowed_lang ? $allowed_lang : ()] unless ref($allowed_lang) eq 'ARRAY';
+
+    if (@$allowed_lang) {
+        my $allowed_regex = join '|', map {
+            my $it = $_;
+            $it =~ tr<-A-Z><_a-z>; # lc, and turn - to _
+            $it =~ tr<_a-z0-9><>cd;  # remove all but a-z0-9_
+            $it;
+        } @$allowed_lang;
+
+        unless ( $current_lang =~ /^$allowed_regex/) {
+            Jifty->log->error("user is requesting $current_lang which is not allowed");
+            my $headers = Jifty->handler->apache->headers_in;
+            use Data::Dumper;
+            Jifty->log->error(Dumper($headers));
+        }
+    }
+
+    open my $fh, '<:encoding(utf-8)',
+        Jifty::Util->absolute_path(
+        File::Spec->catdir(
+            Jifty->config->framework('Web')->{StaticRoot},
+            "js/dict/$current_lang.json" ))
+        or Jifty->log->error("Can't find dictionary file $current_lang.json: $!");
+
+    local $/;
+    my $inline_dict = <$fh> || '{}';
+    # js l10n init
+    Jifty->web->out(qq{<script type="text/javascript">
+Localization.dict_path = '/static/js/dict';
+Localization.dict = $inline_dict;
+</script>});
 }
 
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm	Tue Nov 13 14:05:20 2007
@@ -39,7 +39,7 @@
     my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
     $request_token->load_by_cols(
         token => $token,
-        authorized => 'f',
+        authorized => '',
     );
 
     return $self->validation_error(token => "I don't know of that request token.") unless $request_token->id;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Dispatcher.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Dispatcher.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Dispatcher.pm	Tue Nov 13 14:05:20 2007
@@ -11,7 +11,11 @@
 
 on     POST '/oauth/request_token' => \&request_token;
 before GET  '/oauth/authorize'     => \&authorize;
+on     POST '/oauth/authorize'     => \&authorize_post;
 on     POST '/oauth/access_token'  => \&access_token;
+on          '/oauth/authorized'    => run { redirect '/oauth/authorize' };
+
+before '*' => \&try_oauth;
 
 =head2 abortmsg CODE, MSG
 
@@ -23,7 +27,7 @@
 sub abortmsg {
     my ($code, $msg) = @_;
     Jifty->log->debug($msg) if defined($msg);
-    abort($code || 400);
+    abort($code) if $code;
 }
 
 =head2 request_token
@@ -100,7 +104,7 @@
 
     if ($oauth_params{token}) {
         my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
-        $request_token->load_by_cols(token => $oauth_params{token}, authorized => 'f');
+        $request_token->load_by_cols(token => $oauth_params{token}, authorized => '');
 
         if ($request_token->id) {
             set consumer => $request_token->consumer;
@@ -109,6 +113,22 @@
     }
 }
 
+=head2 authorize_post
+
+The user is submitting an AuthorizeRequestToken action
+
+=cut
+
+sub authorize_post {
+    my $result = Jifty->web->response->result("authorize_request_token");
+    unless ($result && $result->success) {
+        redirect '/oauth/authorize';
+    }
+
+    set result => $result;
+    show '/oauth/authorized';
+}
+
 =head2 access_token
 
 The consumer is trying to trade a request token for an access token
@@ -162,6 +182,8 @@
         if $@ || !defined($token) || !$ok;
 
     $consumer->made_request(@oauth_params{qw/timestamp nonce/});
+    $request_token->set_used('t');
+
     set oauth_response => {
         oauth_token        => $token->token,
         oauth_token_secret => $token->secret
@@ -169,6 +191,68 @@
     show 'oauth/response';
 }
 
+=head2 try_oauth
+
+If this is a protected resource request, see if we can authorize the request
+with an access token.
+
+This is dissimilar to the other OAuth requests because if anything fails, you
+just don't set a current_user, and then the rest of the dispatcher rules will
+take care of it. Thus, failure is handled quite differently in this rule.  We
+try to abort as early as possible to make OAuth less of a hit on all requests.
+
+=cut
+
+sub try_oauth
+{
+    my @params = qw/consumer_key signature_method signature
+                    timestamp nonce token version/;
+    set no_abort => 1;
+    my %oauth_params  = get_parameters(@params);
+    for (@params) {
+        abortmsg(undef, "Undefined required parameter: $_"), return if !defined($oauth_params{$_});
+    }
+
+    my $consumer = get_consumer($oauth_params{consumer_key});
+    return if !$consumer->id;
+    abortmsg(undef, "No known consumer with key $oauth_params{consumer_key}"), return unless $consumer->id;
+
+    my $signature_key = get_signature_key($oauth_params{signature_method}, $consumer);
+    if ($signature_key && ref($signature_key) && !defined($$signature_key)) {
+        abortmsg(undef, "Failed to get signature key.");
+        return;
+    }
+
+    my ($ok, $msg) = $consumer->is_valid_request(@oauth_params{qw/timestamp nonce/});
+    abortmsg(undef, $msg), return if !$ok;
+
+    my $access_token = Jifty::Plugin::OAuth::Model::AccessToken->new(current_user => Jifty::CurrentUser->superuser);
+    $access_token->load_by_cols(consumer => $consumer, token => $oauth_params{token});
+
+    abortmsg(undef, "No token found for consumer ".$consumer->name." with key $oauth_params{token}"), return unless $access_token->id;
+
+    ($ok, $msg) = $access_token->is_valid;
+    abortmsg(undef, "Cannot access protected resources with this access token: $msg"), return if !$ok;
+
+    # Net::OAuth::Request will die hard if it doesn't get everything it wants
+    my $request = eval { Net::OAuth::ProtectedResourceRequest->new(
+        request_url     => Jifty->web->url(path => Jifty->web->request->path),
+        request_method  => Jifty->handler->apache->method(),
+        consumer_secret => $consumer->secret,
+        token_secret    => $access_token->secret,
+        signature_key   => $signature_key,
+
+        map { $_ => $oauth_params{$_} } @params
+    ) };
+
+    abortmsg(undef, "Unable to create ProtectedResourceRequest: $@"), return if $@ || !defined($request);
+
+    abortmsg(undef, "Invalid signature (type: $oauth_params{signature_method})."), return unless $request->verify;
+
+    $consumer->made_request(@oauth_params{qw/timestamp nonce/});
+    Jifty->web->current_user(Jifty->app_class('CurrentUser')->new(id => $access_token->auth_as));
+}
+
 =head2 get_consumer CONSUMER KEY
 
 Helper function to load a consumer by consumer key. Will abort if the key
@@ -180,7 +264,7 @@
     my $key = shift;
     my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
     $consumer->load_by_cols(consumer_key => $key);
-    abortmsg(401, "No known consumer with key $key") if !$consumer->id;
+    abortmsg(401, "No known consumer with key $key") unless $consumer->id || get 'no_abort';
     return $consumer;
 }
 
@@ -190,10 +274,14 @@
 method is unsupported, or if the consumer lacks the prerequisites for this
 signature method.
 
-Will return C<undef> is the signature key is consumer independent, as is the
+Will return C<undef> if the signature key is consumer independent, as is the
 case for C<PLAINTEXT> and C<HMAC-SHA1>. C<RSA-SHA1> depends on the consumer
 having the C<rsa_key> field.
 
+If the signature method is invalid and no_abort is set, it will return a
+special value of a reference to undef. Yes this sucks but undef already has
+an important meaning.
+
 =cut
 
 {
@@ -204,7 +292,9 @@
     sub get_signature_key {
         my ($method, $consumer) = @_;
         if (!$valid_signature_methods{$method}) {
-            abortmsg(400, "Unsupported signature method requested: $method");
+            abortmsg(400, "Unsupported signature method requested: $method")
+                unless get 'no_abort';
+            return \undef;
         }
 
         my $field = $key_field{$method};
@@ -214,8 +304,10 @@
 
         my $key = $consumer->$field;
 
-        abortmsg(400, "Consumer does not have necessary field $field required for signature method $method")
-            unless defined $key;
+        if (!defined $key) {
+            abortmsg(400, "Consumer does not have necessary field $field required for signature method $method") unless get 'no_abort';
+            return;
+        }
 
         if ($method eq 'RSA-SHA1') {
             $key = Crypt::OpenSSL::RSA->new_public_key($key);

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm	Tue Nov 13 14:05:20 2007
@@ -22,8 +22,13 @@
         type is 'timestamp',
         filters are 'Jifty::DBI::Filter::DateTime';
 
+    column token =>
+        type is 'varchar',
+        is required;
+
     column secret =>
-        type is 'varchar';
+        type is 'varchar',
+        is required;
 
     column consumer =>
         refers_to Jifty::Plugin::OAuth::Model::Consumer;
@@ -38,5 +43,27 @@
 
 sub table {'oauth_access_tokens'}
 
+=head2 is_valid
+
+This neatly encapsulates the "is this access token perfect?" check.
+
+This will return a (boolean, message) pair, with boolean indicating success
+(true means the token is good) and message indicating error (or another
+affirmation of success).
+
+=cut
+
+sub is_valid {
+    my $self = shift;
+
+    return (0, "Access token has no authorizing user")
+        if !$self->auth_as;
+
+    return (0, "Access token expired")
+        if $self->valid_until < DateTime->now;
+
+    return (1, "Request token valid");
+}
+
 1;
 

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/Consumer.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/Consumer.pm	Tue Nov 13 14:05:20 2007
@@ -118,7 +118,7 @@
 sub made_request {
     my ($self, $timestamp, $nonce) = @_;
     $self->set_last_timestamp($timestamp);
-    $self->nonces->{$nonce} = 1;
+    $self->set_nonces({ %{$self->nonces}, $nonce => 1 });
 }
 
 1;

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm	Tue Nov 13 14:05:20 2007
@@ -20,7 +20,7 @@
 
     column authorized =>
         type is 'boolean',
-        default is 'f';
+        default is '';
 
     # kludge 2: this kind of plugin cannot yet casually refer_to app models
     column authorized_by =>
@@ -31,9 +31,11 @@
         refers_to Jifty::Plugin::OAuth::Model::Consumer,
         is required;
 
+    # kludge 3: Jifty::DBI + SQLite = poor boolean handling
+    # so the empty string is the false value, 't' is the true value
     column used =>
         type is 'boolean',
-        default is 'f';
+        default is '';
 
     column token =>
         type is 'varchar',
@@ -55,13 +57,15 @@
 
 =head2 after_set_authorized
 
-This will set the C<authorized_by> to the current user.
+This will set the C<authorized_by> to the current user. It will also refresh
+the valid_until to be active for another hour.
 
 =cut
 
 sub after_set_authorized {
     my $self = shift;
     $self->set_authorized_by(Jifty->web->current_user->id);
+    $self->set_valid_until(DateTime->now->add(hours => 1));
 }
 
 =head2 can_trade_for_access_token

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/View.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/View.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/OAuth/View.pm	Tue Nov 13 14:05:20 2007
@@ -104,9 +104,6 @@
     );
 
     Jifty->web->form->start();
-    Jifty->web->form->next_page(url => "/oauth/authorized");
-
-    outs $authorize->hidden(callback => get 'callback');
 
     # if the site put the token in the request, then use it
     # otherwise, prompt the user for it
@@ -119,6 +116,8 @@
         $authorize->form_field('token')->render;
     }
 
+    outs_raw $authorize->hidden(callback => get 'callback');
+
     outs_raw($authorize->button(
         label => 'Allow',
         arguments => { %args, authorize => 'allow' },
@@ -141,7 +140,7 @@
 
 template 'oauth/authorized' => page { title => 'XXX' }
 content {
-    my $result    = Jifty->web->response->result('authorize_request_token');
+    my $result    = get 'result';
     my $callback  = $result->content('callback');
     my $token     = $result->content('token');
     my $token_obj = $result->content('token_obj');
@@ -159,7 +158,7 @@
 
         p {
             outs 'To return to ';
-            show 'oauth/consumer';
+            show '/oauth/consumer';
             outs ', ';
             hyperlink(
                 label => 'click here',

Modified: jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Plugin/SinglePage.pm	Tue Nov 13 14:05:20 2007
@@ -25,6 +25,9 @@
     return if $self->_pre_init;
 
     Jifty::Web::Form::Clickable->add_trigger( before_new => _sp_link($self));
+    Jifty::Web::Form::Clickable->add_trigger( name      => 'before_state_variable',
+                                              callback  => _filter_page_region_vars($self),
+                                              abortable => 1 );
     my %opt = @_;
     $self->region_name($opt{region_name} || '__page');
 }
@@ -37,6 +40,17 @@
     push @{$args->{onclick}}, @_ if @_;
 }
 
+sub _filter_page_region_vars {
+    my $self = shift;
+    return sub {
+        my ( $clickable, $key, $value ) = @_;
+        if ($key eq 'region-'.$self->region_name || $key =~ m/^region-\Q$self->{region_name}\E\./) {
+            return 0;
+        }
+        return 1;
+    }
+}
+
 sub _sp_link {
     my $self = shift;
     return sub {
@@ -46,7 +60,7 @@
             $self->_push_onclick($args, {
                 region       => $self->region_name,
                 replace_with => $url,
-                args         => $args->{parameters}});
+                args         => { %{$args->{parameters}}} });
         }
         elsif (exists $args->{submit} && !$args->{onclick}) {
 	    if ($args->{_form} && $args->{_form}{submit_to}) {

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 Nov 13 14:05:20 2007
@@ -28,9 +28,8 @@
     my $self = shift;
     my %args = (@_);
     $self->_get_current_user(%args);
-    
-    $self->SUPER::_init(@_);
 
+    $self->SUPER::_init(@_);
 }
 
 =head1 METHODS
@@ -41,8 +40,8 @@
 
 C<create> can be called as either a class method or an object method.
 
-Takes an array of key-value pairs and inserts a new row into the database representing
-this object.
+Takes an array of key-value pairs and inserts a new row into the
+database representing this object.
 
 Override's L<Jifty::DBI::Record> in these ways:
 
@@ -159,9 +158,8 @@
 
 =cut
 
-sub _primary_key { 'id' }
-sub id { $_[0]->{'values'}->{'id'} }
-
+sub _primary_key {'id'}
+sub id           { $_[0]->{'values'}->{'id'} }
 
 =head2 load_or_create
 
@@ -174,8 +172,8 @@
 sub load_or_create {
     my $class = shift;
     my $self;
-    if (ref($class)) {
-       ($self,$class) = ($class,undef); 
+    if ( ref($class) ) {
+        ( $self, $class ) = ( $class, undef );
     } else {
         $self = $class->new();
     }
@@ -187,44 +185,47 @@
         return $self->create(%args);
     }
 
-    return ($id,$msg);
+    return ( $id, $msg );
 }
 
-
 =head2 as_create_action PARAMHASH
 
-Returns the L<Jifty::Action::Record::Create> action for this model class.
+Returns the L<Jifty::Action::Record::Create> action for this model
+class.
 
-The PARAMHASH allows you to add additional parameters to pass to L<Jifty::Web/new_action>.
+The PARAMHASH allows you to add additional parameters to pass to
+L<Jifty::Web/new_action>.
 
 =cut
 
 sub _action_from_record {
-    my $self = shift;
-    my $verb = shift;
+    my $self  = shift;
+    my $verb  = shift;
     my $class = ref $self || $self;
     $class =~ s/::Model::/::Action::$verb/;
     return $class;
 }
 
 sub as_create_action {
-    my $self = shift;
+    my $self         = shift;
     my $action_class = $self->_action_from_record('Create');
     return Jifty->web->new_action( class => $action_class, @_ );
 }
 
 =head2 as_update_action PARAMHASH
 
-Returns the L<Jifty::Action::Record::Update> action for this model class. The current record is passed to the constructor.
+Returns the L<Jifty::Action::Record::Update> action for this model
+class. The current record is passed to the constructor.
 
-The PARAMHASH allows you to add additional parameters to pass to L<Jifty::Web/new_action>.
+The PARAMHASH allows you to add additional parameters to pass to
+L<Jifty::Web/new_action>.
 
 =cut
 
 sub as_update_action {
-    my $self = shift;
+    my $self         = shift;
     my $action_class = $self->_action_from_record('Update');
-    return Jifty->web->new_action( 
+    return Jifty->web->new_action(
         class  => $action_class,
         record => $self,
         @_,
@@ -233,14 +234,16 @@
 
 =head2 as_delete_action PARAMHASH
 
-Returns the L<Jifty::Action::Record::Delete> action for this model class. The current record is passed to the constructor.
+Returns the L<Jifty::Action::Record::Delete> action for this model
+class. The current record is passed to the constructor.
 
-The PARAMHASH allows you to add additional parameters to pass to L<Jifty::Web/new_action>.
+The PARAMHASH allows you to add additional parameters to pass to
+L<Jifty::Web/new_action>.
 
 =cut
 
 sub as_delete_action {
-    my $self = shift;
+    my $self         = shift;
     my $action_class = $self->_action_from_record('Delete');
     return Jifty->web->new_action(
         class  => $action_class,
@@ -251,35 +254,40 @@
 
 =head2 as_search_action PARAMHASH
 
-Returns the L<Jifty::Action::Record::Search> action for this model class.
+Returns the L<Jifty::Action::Record::Search> action for this model
+class.
 
-The PARAMHASH allows you to add additional parameters to pass to L<Jifty::Web/new_action>.
+The PARAMHASH allows you to add additional parameters to pass to
+L<Jifty::Web/new_action>.
 
 =cut
 
 sub as_search_action {
-    my $self = shift;
+    my $self         = shift;
     my $action_class = $self->_action_from_record('Search');
     return Jifty->web->new_action(
-        class  => $action_class,
+        class => $action_class,
         @_,
     );
 }
 
 =head2 _guess_table_name
 
-Guesses a table name based on the class's last part. In addition to the work performed in L<Jifty::DBI::Record>, this method also prefixes the table name with the plugin table prefix, if the model belongs to a plugin.
+Guesses a table name based on the class's last part. In addition to
+the work performed in L<Jifty::DBI::Record>, this method also prefixes
+the table name with the plugin table prefix, if the model belongs to a
+plugin.
 
 =cut
 
 sub _guess_table_name {
-    my $self = shift;
+    my $self  = shift;
     my $table = $self->SUPER::_guess_table_name;
 
     # Add plugin table prefix if a plugin model
     my $class = ref($self) ? ref($self) : $self;
     my $app_plugin_root = Jifty->app_class('Plugin');
-    if ($class =~ /^(?:Jifty::Plugin::|$app_plugin_root)/) {
+    if ( $class =~ /^(?:Jifty::Plugin::|$app_plugin_root)/ ) {
 
         # Guess the plugin class name
         my $plugin_class = $class;
@@ -289,13 +297,14 @@
         my ($plugin) = grep { ref $_ eq $plugin_class } Jifty->plugins;
 
         # Add the prefix if found
-        if (defined $plugin) {
+        if ( defined $plugin ) {
             $table = $plugin->table_prefix . $table;
         }
 
         # Uh oh. Warn, but try to keep going.
         else {
-            warn "Model $class looks like a plugin model, but $plugin_class could not be found.";
+            warn
+                "Model $class looks like a plugin model, but $plugin_class could not be found.";
         }
     }
 
@@ -333,7 +342,8 @@
 
 =back
 
-Models wishing to customize authorization checks should override this method. You can do so like this:
+Models wishing to customize authorization checks should override this
+method. You can do so like this:
 
   sub current_user_can {
       my ($self, $right, %args) = @_;
@@ -346,7 +356,9 @@
       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.
+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
 
@@ -356,24 +368,25 @@
 
 =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.
+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>.
+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.  
+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).  
+L<Jifty::RightsFrom>, the access control decision is deferred to
+another object (via the C<delegate_current_user_can> subroutine).
 
 =item 5.
 
@@ -383,7 +396,9 @@
 
 =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>:
+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',
@@ -391,15 +406,21 @@
       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:
+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.
+If a handler is abortable and aborts by returning a false value (such
+as C<undef>), C<current_user_can> returns false.
 
 =item 2.
 
@@ -407,11 +428,14 @@
 
 =item 3.
 
-If any handler returns 'allow' and no handler returns 'deny', C<current_user_can> returns true.
+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.
+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
 
@@ -420,38 +444,38 @@
 sub current_user_can {
     my $self  = shift;
     my $right = shift;
-    
+
     # Turn off access control for the whole application
-    if (Jifty->config->framework('SkipAccessControl')) {
-	    return 1;	
+    if ( Jifty->config->framework('SkipAccessControl') ) {
+        return 1;
     }
 
     my $hook_status = $self->call_trigger( before_access => $right, @_ );
 
     # If not aborted...
-    if (defined $hook_status) {
+    if ( defined $hook_status ) {
 
         # Compile the handler results
         my %results;
-        $results{ $_->[0] }++ for (@{ $self->last_trigger_results });
+        $results{ $_->[0] }++ for ( @{ $self->last_trigger_results } );
 
         # Deny always takes precedent
-        if ($results{deny}) {
+        if ( $results{deny} ) {
             return 0;
         }
 
         # Then allow...
-        elsif ($results{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 )
@@ -459,12 +483,11 @@
         return (1);
     }
 
-    
-    if ($self->can('delegate_current_user_can')) {
-        return $self->delegate_current_user_can($right, @_); 
+    if ( $self->can('delegate_current_user_can') ) {
+        return $self->delegate_current_user_can( $right, @_ );
     }
 
-    unless ( $self->current_user->isa( 'Jifty::CurrentUser' ) ) {
+    unless ( $self->current_user->isa('Jifty::CurrentUser') ) {
         $self->log->error(
             "Hm. called to authenticate without a currentuser - "
                 . $self->current_user );
@@ -480,8 +503,7 @@
 
 =cut
 
-sub check_create_rights { return shift->current_user_can('create', @_) }
-
+sub check_create_rights { return shift->current_user_can( 'create', @_ ) }
 
 =head2 check_read_rights
 
@@ -503,8 +525,7 @@
 
 =cut
 
-sub check_update_rights { return shift->current_user_can('update', @_) } 
-
+sub check_update_rights { return shift->current_user_can( 'update', @_ ) }
 
 =head2 check_delete_rights
 
@@ -512,24 +533,22 @@
 
 =cut
 
-sub check_delete_rights { return shift->current_user_can('delete', @_) }
-
+sub check_delete_rights { return shift->current_user_can( 'delete', @_ ) }
 
 sub _set {
     my $self = shift;
 
-    unless ($self->check_update_rights(@_)) {
-        return (0, _('Permission denied'));
+    unless ( $self->check_update_rights(@_) ) {
+        return ( 0, _('Permission denied') );
     }
     $self->SUPER::_set(@_);
 }
 
-    
 sub _value {
-    my $self = shift;
+    my $self   = shift;
     my $column = shift;
 
-    unless ($self->check_read_rights( $column => @_ )) {
+    unless ( $self->check_read_rights( $column => @_ ) ) {
         return (undef);
     }
     my $value = $self->SUPER::_value( $column => @_ );
@@ -539,7 +558,6 @@
     $value;
 }
 
-
 =head2 as_superuser
 
 Returns a copy of this object with the current_user set to the
@@ -551,43 +569,11 @@
 sub as_superuser {
     my $self = shift;
 
-    my $clone = $self->new(current_user => $self->current_user->superuser);
-    $clone->load($self->id);
+    my $clone = $self->new( current_user => $self->current_user->superuser );
+    $clone->load( $self->id );
     return $clone;
 }
 
-
-=head2 _collection_value METHOD
-
-A method ripped from the pages of Jifty::DBI::Record 
-so we could change the invocation method of the collection generator to
-add a current_user argument.
-
-=cut
-
-sub _collection_value {
-    my $self = shift;
-
-    my $method_name = shift;
-    return unless defined $method_name;
-
-    my $column    = $self->column($method_name);
-    my $classname = $column->refers_to();
-
-    return undef unless $classname;
-    return unless $classname->isa( 'Jifty::DBI::Collection' );
-
-    if ( my $prefetched_collection = $self->_prefetched_collection($method_name)) {
-        return $prefetched_collection;
-    }
-
-    my $coll = $classname->new( current_user => $self->current_user );
-    if ($column->by and $self->id) { 
-            $coll->limit( column => $column->by(), value => $self->id );
-    }
-    return $coll;
-}
-
 =head2 delete PARAMHASH
 
 Overrides L<Jifty::DBI::Record> to check the delete ACL.
@@ -596,11 +582,11 @@
 
 sub delete {
     my $self = shift;
-    unless ($self->check_delete_rights(@_)) {
-            Jifty->log->logcluck("Permission denied");
-            return(0, _('Permission denied'));
-        }
-    $self->SUPER::delete(@_); 
+    unless ( $self->check_delete_rights(@_) ) {
+        Jifty->log->logcluck("Permission denied");
+        return ( 0, _('Permission denied') );
+    }
+    $self->SUPER::delete(@_);
 }
 
 =head2 brief_description
@@ -610,48 +596,45 @@
 =cut
 
 sub brief_description {
-    my $self = shift;
+    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 
+When displaying a list of records, Jifty can display a friendly value
 rather than the column's unique id.  Out of the box, Jifty always
-tries to display the 'name' field from the record. You can override this
-method to return the name of a method on your record class which will
-return a nice short human readable description for this record.
+tries to display the 'name' field from the record. You can override
+this method to return the name of a method on your record class which
+will return a nice short human readable description for this record.
 
 =cut
 
 sub _brief_description {'name'}
 
-=head2 _to_record
+=head2 _new_collection_args
 
-This is the Jifty::DBI function that is called when you fetch a value which C<REFERENCES> a
-Record class.  The only change from the Jifty::DBI code is the arguments to C<new>.
+Overrides the default arguments which this collection passes to new
+collections, to pass the C<current_user>.
 
 =cut
 
-sub _to_record {
-    my $self  = shift;
-    my $column_name = shift;
-    my $value = shift;
+sub _new_collection_args {
+    my $self = shift;
+    return ( current_user => $self->current_user );
+}
 
-    my $column = $self->column($column_name);
-    my $classname = $column->refers_to();
+=head2 _new_record_args
 
-    return undef unless $classname;
-    return unless $classname->isa( 'Jifty::Record' );
+Overrides the default arguments which this collection passes to new
+records, to pass the C<current_user>.
 
-    # XXX TODO FIXME we need to figure out the right way to call new here
-    # perhaps the handle should have an initiializer for records/collections
-    my $object = $classname->new(current_user => $self->current_user);
-    $object->load_by_cols(( $column->by || 'id')  => $value) if ($value);
-    # XXX: an attribute or hook to let model class declare implicit
-    # readable refers_to columns.  $object->_is_readable(1) if $column->blah;
-    return $object;
+=cut
+
+sub _new_record_args {
+    my $self = shift;
+    return ( current_user => $self->current_user );
 }
 
 =head2 cache_key_prefix
@@ -661,7 +644,6 @@
 
 =cut
 
-
 sub cache_key_prefix {
     Jifty->config->framework('Database')->{'Database'};
 }
@@ -673,21 +655,20 @@
 }
 
 =head2 since
- 
-By default, all models exist since C<undef>, the ur-time when the application was created. Please override it for your model class.
- 
-=cut
- 
 
+By default, all models exist since C<undef>, the ur-time when the
+application was created. Please override it for your model class.
+
+=cut
 
 =head2 printable_table_schema
 
-When called, this method will generate the SQL schema for the current version of this 
-class and return it as a scalar, suitable for printing or execution in your database's command line.
+When called, this method will generate the SQL schema for the current
+version of this class and return it as a scalar, suitable for printing
+or execution in your database's command line.
 
 =cut
 
-
 sub printable_table_schema {
     my $class = shift;
 
@@ -697,8 +678,9 @@
 
 =head2 create_table_in_db
 
-When called, this method will generate the SQL schema for the current version of this 
-class and insert it into the application's currently open database.
+When called, this method will generate the SQL schema for the current
+version of this class and insert it into the application's currently
+open database.
 
 =cut
 
@@ -731,9 +713,10 @@
     $ret or die "error removing table $self: " . $ret->error_message;
 }
 
-sub _make_schema { 
+sub _make_schema {
     my $class = shift;
 
+    require Jifty::DBI::SchemaGenerator;
     my $schema_gen = Jifty::DBI::SchemaGenerator->new( Jifty->handle )
         or die "Can't make Jifty::DBI::SchemaGenerator";
     my $ret = $schema_gen->add_model( $class->new );
@@ -744,7 +727,8 @@
 
 =head2 add_column_sql column_name
 
-Returns the SQL statement necessary 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
 
@@ -753,11 +737,11 @@
     my $column_name = shift;
 
     my $col        = $self->column($column_name);
-    my $definition = $self->_make_schema()->column_definition_sql($self->table => $col->name);
+    my $definition = $self->_make_schema()
+        ->column_definition_sql( $self->table => $col->name );
     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.
@@ -766,14 +750,19 @@
 
 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;
+    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 necessary 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
 
@@ -807,14 +796,20 @@
 
 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;
+    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.
+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.
 
 Jifty's notion of the schema version is currently broken into two:
 
@@ -822,11 +817,14 @@
 
 =item 1.
 
-The Jifty version is the first. In the case of models defined by Jifty itself, these use the version found in C<$Jifty::VERSION>.
+The Jifty version is the first. In the case of models defined by Jifty
+itself, these use the version found in C<$Jifty::VERSION>.
 
 =item 2.
 
-Any model defined by your application use the database version declared in the configuration. In F<etc/config.yml>, this is lcoated at:
+Any model defined by your application use the database version
+declared in the configuration. In F<etc/config.yml>, this is lcoated
+at:
 
   framework:
     Database:
@@ -834,15 +832,17 @@
 
 =back
 
-A model is considered to be defined by Jifty if it the package name starts with "Jifty::". Otherwise, it is assumed to be an application model.
+A model is considered to be defined by Jifty if it the package name
+starts with "Jifty::". Otherwise, it is assumed to be an application
+model.
 
 =cut
 
 sub schema_version {
     my $class = shift;
-    
+
     # Return the Jifty schema version
-    if ($class =~ /^Jifty::Model::/) {
+    if ( $class =~ /^Jifty::Model::/ ) {
         return $Jifty::VERSION;
     }
 
@@ -856,4 +856,3 @@
 }
 
 1;
-

Modified: jifty/branches/virtual-models/lib/Jifty/Test.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Test.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Test.pm	Tue Nov 13 14:05:20 2007
@@ -69,9 +69,9 @@
 sub is_passing {
     my $tb = Jifty::Test->builder;
 
-    my $is_failing = 0;
-    $is_failing ||= grep {not $_} $tb->summary;
-    $is_failing ||= ($tb->has_plan || '') eq 'no_plan'
+    my $is_failing = grep {not $_} $tb->summary;
+    no warnings 'uninitialized';
+    $is_failing ||= $tb->has_plan eq 'no_plan'
                       ? 0
                       : $tb->expected_tests < $tb->current_test;
 
@@ -92,7 +92,9 @@
 
 sub is_done {
     my $tb = Jifty::Test->builder;
-    if( ($tb->has_plan || '') eq 'no_plan' ) {
+
+    no warnings 'uninitialized';
+    if( $tb->has_plan eq 'no_plan' ) {
         return $tb->current_test > 0;
     }
     else {

Modified: jifty/branches/virtual-models/lib/Jifty/Test/WWW/Mechanize.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Test/WWW/Mechanize.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Test/WWW/Mechanize.pm	Tue Nov 13 14:05:20 2007
@@ -416,8 +416,11 @@
     # Remove reason from end if it's there
     pop @_ if @_ % 2;
 
-    carp("Couldn't find link") unless
-      $self->follow_link(@_);
+    # Test::WWW::Mechanize allows passing in a hashref of arguments, so we should to
+    if  ( ref($_[0]) eq 'HASH') {
+        @_ = %{$_[0]};
+    }
+    carp("Couldn't find link") unless $self->follow_link(@_);
     {
         local $Test::Builder::Level = $Test::Builder::Level;
         $Test::Builder::Level++;

Modified: jifty/branches/virtual-models/lib/Jifty/Test/WWW/Selenium.pm
==============================================================================
--- jifty/branches/virtual-models/lib/Jifty/Test/WWW/Selenium.pm	(original)
+++ jifty/branches/virtual-models/lib/Jifty/Test/WWW/Selenium.pm	Tue Nov 13 14:05:20 2007
@@ -22,8 +22,11 @@
 
 L<Jifty::Test::WWW::Selenium> creates a L<Test::WWW::Selenium> object
 associated with your jifty application to test.  In addition, it
-starts selenium remote control for you, unless SELENIUM_RC_SERVER is
-specified when the test is run.
+starts selenium remote control for you, unless C<SELENIUM_RC_SERVER>
+is specified when the test is run.  You might also want to set
+C<SELENIUM_RC_TEST_AGAINST> to your local IP address so
+C<SELENIUM_RC_SERVER> can test against you.  C<SELENIUM_RC_BROWSER>
+tells the rc server what browser to run the tests with.
 
 =head2 rc_ok
 
@@ -59,9 +62,9 @@
 	}
     }
 
-    $args{browser_url} ||= 'http://localhost:'.$server->port;
+    $args{browser_url} ||= 'http://'.($ENV{SELENIUM_RC_TEST_AGAINST} || $args{test_server} || 'localhost').':'.$server->port;
 
-    $args{browser} ||= $class->_get_default_browser;
+    $args{browser} ||= $ENV{SELENIUM_RC_BROWSER} || $class->_get_default_browser;
 
     $SIG{CHLD} = \&_REAPER;
 
@@ -115,7 +118,7 @@
 	Test::More::diag "start selenium rc [$$]";
 	local $SIG{CHLD} = \&_REAPER;
 	local $SIG{TERM} = sub { exit 0 };
-	Alien::SeleniumRC::start();
+	Alien::SeleniumRC::start(@{ $args{args} || [] });
 	Test::More::diag "selenium rc [$$] finished.";
 	exit;
     }

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 Nov 13 14:05:20 2007
@@ -76,7 +76,7 @@
     };
     
     my $content = Template::Declare::Tags::show_page( $template, Jifty->web->request->arguments );
-    return unless defined $content && length $content;
+    return unless defined $content;
 
     my $r = Jifty->handler->apache;
     $r->content_type || $r->content_type('text/html; charset=utf-8'); # Set up a default

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 Nov 13 14:05:20 2007
@@ -302,6 +302,9 @@
 
 sub state_variable {
     my $self = shift;
+    defined $self->call_trigger('before_state_variable', @_)
+        or return; # if aborted by trigger
+
     my ( $key, $value, $fallback ) = @_;
     if ( defined $value and length $value ) {
         $self->{state_variable}{"J:V-$key"} = $value;

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 Nov 13 14:05:20 2007
@@ -522,7 +522,7 @@
                 if ($region) {
                     @args{qw/mode path region/} = ('Replace', $region->path, $region->qualified_name);
                 } else {
-                    $self->log->debug("Can't find region ".$hook->{refresh});
+                    $self->log->warn("Can't find region ".$hook->{refresh});
                     @args{qw/mode path region/} = ('Replace', undef, $hook->{refresh});
                 }
             } elsif ((exists $hook->{refresh_self} and Jifty->web->current_region) or (Jifty->web->current_region and $hook->{args} and %{$hook->{args}})) {

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 Nov 13 14:05:20 2007
@@ -36,7 +36,7 @@
 The path to the fragment that this page region contains.  Defaults to
 C</__jifty/empty>, which, as its name implies, is empty.
 
-=item defaults (optional)
+=item arguments (optional) (formerly 'defaults')
 
 Specifies an optional set of parameter defaults.  These should all be
 simple scalars, as they might be passed across HTTP if AJAX is used.
@@ -82,6 +82,9 @@
                 @_
                );
 
+
+    $args{'arguments'} ||= delete $args{'defaults'};
+
     # Name is required
     if (not defined $args{name}) {
         warn "Name is required for page regions.";
@@ -89,16 +92,16 @@
     }
 
     # References don't go over HTTP very well
-    if (grep {ref $_} values %{$args{defaults}}) {
-        warn "Reference '$args{defaults}{$_}' passed as default for '$_' to region '$args{name}'"
-          for grep {ref $args{defaults}{$_}} keys %{$args{defaults}};
+    if (grep {ref $_} values %{$args{arguments}}) {
+        warn "Reference '$args{arguments}{$_}' passed as default for '$_' to region '$args{name}'"
+          for grep {ref $args{arguments}{$_}} keys %{$args{arguments}};
         return;
     }
 
     $self->name($args{name});
     $self->qualified_name(Jifty->web->qualified_region($self));
     $self->default_path($args{path});
-    $self->default_arguments($args{defaults});
+    $self->default_arguments($args{arguments});
     $self->force_arguments($args{force_arguments});
     $self->force_path($args{force_path});
     $self->arguments({});

Modified: jifty/branches/virtual-models/t/99-pod-coverage.t
==============================================================================
--- jifty/branches/virtual-models/t/99-pod-coverage.t	(original)
+++ jifty/branches/virtual-models/t/99-pod-coverage.t	Tue Nov 13 14:05:20 2007
@@ -1,6 +1,7 @@
 use Test::More;
 eval "use Test::Pod::Coverage 1.00";
 plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" if $@;
+plan skip_all => "Coverage tests only run for authors" unless (-d 'inc/.author');
 all_pod_coverage_ok( );
 
 # Workaround for dumb bug (fixed in 5.8.7) where Test::Builder thinks that

Modified: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm	Tue Nov 13 14:05:20 2007
@@ -7,23 +7,34 @@
 use MIME::Base64;
 use Crypt::OpenSSL::RSA;
 use Digest::HMAC_SHA1 'hmac_sha1';
+use Jifty::Test::WWW::Mechanize;
 
-our @EXPORT = qw($timestamp $url $mech $pubkey $seckey $token_obj
-                 response_is sign get_latest_token
-                 allow_ok deny_ok _authorize_request_token);
-
-sub setup {
-    my $class = shift;
-    $class->SUPER::setup;
-    $class->export_to_level(1);
-}
+our @EXPORT = qw($timestamp $url $umech $cmech $pubkey $seckey $token_obj
+                 $server $URL response_is sign get_latest_token allow_ok deny_ok
+                 _authorize_request_token get_request_token get_authorized_token
+                 get_access_token);
 
 our $timestamp = 0;
 our $url;
-our $mech;
+our $umech;
+our $cmech;
 our $pubkey = slurp('t/id_rsa.pub');
 our $seckey = slurp('t/id_rsa');
 our $token_obj;
+our $server;
+our $URL;
+
+sub setup {
+    my $class = shift;
+    $class->SUPER::setup;
+    $class->export_to_level(1);
+
+    $server  = Jifty::Test->make_server;
+    $URL     = $server->started_ok;
+    $umech   = Jifty::Test::WWW::Mechanize->new();
+    $cmech   = Jifty::Test::WWW::Mechanize->new();
+    $url     = $URL . '/oauth/request_token';
+}
 
 sub response_is {
     ++$timestamp;
@@ -41,6 +52,9 @@
         @_,
     );
 
+    local $url = $URL . delete $params{url}
+        if $params{url};
+
     for (grep {!defined $params{$_}} keys %params) {
         delete $params{$_};
     }
@@ -52,37 +66,46 @@
     my $consumer_secret = delete $params{consumer_secret}
         or die "consumer_secret not passed to response_is!";
 
+    if ($url !~ /request_token/) {
+        $token_secret ||= $token_obj->secret;
+        $params{oauth_token} ||= $token_obj->token;
+    }
+
     $params{oauth_signature} ||= sign($method, $token_secret, $consumer_secret, %params);
 
     my $r;
 
     if ($method eq 'POST') {
-        $r = $mech->post($url, [%params]);
+        $r = $cmech->post($url, [%params]);
     }
     else {
         my $query = join '&',
                     map { "$_=" . Jifty->web->escape_uri($params{$_}||'') }
                     keys %params;
-        $r = $mech->get("$url?$query");
+        $r = $cmech->get("$url?$query");
     }
 
     local $Test::Builder::Level = $Test::Builder::Level + 1;
     main::is($r->code, $code, $testname);
 
-    undef $token_obj;
-    get_latest_token();
-    if ($code == 200) {
-        main::ok($token_obj, "Successfully loaded a token object with token ".$token_obj->token.".");
-    }
-    else {
-        main::ok(!$token_obj, "Did not get a token");
+    if ($url =~ /oauth/) {
+        undef $token_obj;
+        get_latest_token();
+        if ($code == 200) {
+            main::ok($token_obj, "Successfully loaded a token object with token ".$token_obj->token.".");
+        }
+        else {
+            main::ok(!$token_obj, "Did not get a token");
+        }
     }
+
+    return $cmech->content;
 }
 
 sub sign {
     my ($method, $token_secret, $consumer_secret, %params) = @_;
 
-    local $url = delete $params{url} || $url;
+    local $url = delete $params{sign_url} || $url;
 
     my $key = delete $params{signature_key};
     my $sig_method = $params{oauth_signature_method} || delete $params{_signature_method};
@@ -144,7 +167,7 @@
 }
 
 sub get_latest_token {
-    my $content = $mech->content;
+    my $content = $cmech->content;
 
     $content =~ s/\boauth_token=(\w+)//
         or return;
@@ -159,14 +182,14 @@
 
     my $package = 'Jifty::Plugin::OAuth::Model::';
 
-    if ($mech->uri =~ /request_token/) {
+    if ($cmech->uri =~ /request_token/) {
         $package .= 'RequestToken';
     }
-    elsif ($mech->uri =~ /access_token/) {
+    elsif ($cmech->uri =~ /access_token/) {
         $package .= 'AccessToken';
     }
     else {
-        Jifty->log->error("Called get_latest_token, but I cannot grok the URI " . $mech->uri);
+        Jifty->log->error("Called get_latest_token, but I cannot grok the URI " . $cmech->uri);
         return;
     }
 
@@ -188,7 +211,7 @@
     ok(0, $error), return if $error;
 
     my $name = $token_obj->consumer->name;
-    $mech->content_contains("Allowing $name to access your stuff");
+    $umech->content_contains("Allowing $name to access your stuff");
 }
 
 sub deny_ok {
@@ -198,7 +221,7 @@
     ok(0, $error), return if $error;
 
     my $name = $token_obj->consumer->name;
-    $mech->content_contains("Denying $name the right to access your stuff");
+    $umech->content_contains("Denying $name the right to access your stuff");
 }
 
 sub _authorize_request_token {
@@ -210,18 +233,53 @@
     my $token = shift || $token_obj->token;
     $token = $token->token if ref $token;
 
-    $mech->get('/oauth/authorize')
+    $umech->get('/oauth/authorize')
         or return "Unable to navigate to /oauth/authorize";;
-    $mech->content =~ /If you trust this application/
+    $umech->content =~ /If you trust this application/
         or return "Content did not much qr/If you trust this application/";
-    my $moniker = $mech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken')
+    my $moniker = $umech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken')
         or return "Unable to find moniker for AuthorizeRequestToken";
-    $mech->fill_in_action($moniker, token => $token)
+    $umech->fill_in_action($moniker, token => $token)
         or return "Unable to fill in the AuthorizeRequestToken action";
-    $mech->click_button(value => $which_button)
+    $umech->click_button(value => $which_button)
         or return "Unable to click $which_button button";
     return;
 }
 
+sub get_request_token {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    response_is(
+        url                    => '/oauth/request_token',
+        code                   => 200,
+        testname               => "200 - plaintext signature",
+        consumer_secret        => 'bar',
+        oauth_consumer_key     => 'foo',
+        oauth_signature_method => 'PLAINTEXT',
+        @_,
+    );
+    return $token_obj;
+}
+
+sub get_authorized_token {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+    get_request_token(@_);
+    allow_ok();
+    return $token_obj;
+}
+
+sub get_access_token {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+    get_authorized_token();
+    response_is(
+        url                    => '/oauth/access_token',
+        code                   => 200,
+        testname               => "200 - plaintext signature",
+        consumer_secret        => 'bar',
+        oauth_consumer_key     => 'foo',
+        oauth_signature_method => 'PLAINTEXT',
+    );
+}
+
 1;
 

Added: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/View.pm	Tue Nov 13 14:05:20 2007
@@ -0,0 +1,12 @@
+#!/usr/bin/env perl
+package TestApp::Plugin::OAuth::View;
+use strict;
+use warnings;
+use Jifty::View::Declare -base;
+
+template '/nuke/the/whales' => page {
+    h1 { "Press the shiny red button." }
+};
+
+1;
+

Modified: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/00-test-setup.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/00-test-setup.t	(original)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/00-test-setup.t	Tue Nov 13 14:05:20 2007
@@ -2,18 +2,20 @@
 use warnings;
 use strict;
 
+use Test::More;
+BEGIN {
+    if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+        plan tests => 10;
+    }
+    else {
+        plan skip_all => "Net::OAuth isn't installed";
+    }
+}
+
 use lib 't/lib';
 use Jifty::SubTest;
-
 use TestApp::Plugin::OAuth::Test;
 
-if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
-    plan tests => 9;
-}
-else {
-    plan skip_all => "Net::OAuth isn't installed";
-}
-
 # sign PLAINTEXT {{{
 is(sign('POST', 'jjd999tj88uiths3', 'djr9rjt0jd78jf88',
         oauth_signature_method => 'PLAINTEXT'),
@@ -30,7 +32,7 @@
     'GET',
     'pfkkdhi9sl3r4s00',
     'kd94hf93k423kf44',
-    url => 'http://photos.example.net/photos',
+    sign_url => 'http://photos.example.net/photos',
     oauth_consumer_key => 'dpf43f3p2l4k3l03',
     oauth_signature_method => 'HMAC-SHA1',
     oauth_timestamp => '1191242096',
@@ -49,7 +51,7 @@
     'GET',
     'pfkkdhi9sl3r4s00',
     'kd94hf93k423kf44',
-    url => 'http://photos.example.net/photos',
+    sign_url => 'http://photos.example.net/photos',
     signature_key => $seckey,
     oauth_consumer_key => 'dpf43f3p2l4k3l03',
     oauth_signature_method => 'RSA-SHA1',

Modified: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/01-basic.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/01-basic.t	(original)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/01-basic.t	Tue Nov 13 14:05:20 2007
@@ -2,18 +2,21 @@
 use warnings;
 use strict;
 
+use Test::More;
+BEGIN {
+    if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+        plan tests => 9;
+    }
+    else {
+        plan skip_all => "Net::OAuth isn't installed";
+    }
+}
+
 use lib 't/lib';
 use Jifty::SubTest;
 
 use Jifty::Test;
 
-if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
-    plan tests => 9;
-}
-else {
-    plan skip_all => "Net::OAuth isn't installed";
-}
-
 use Jifty::Test::WWW::Mechanize;
 
 my $server  = Jifty::Test->make_server;

Modified: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/02-request-token.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/02-request-token.t	(original)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/02-request-token.t	Tue Nov 13 14:05:20 2007
@@ -2,26 +2,21 @@
 use warnings;
 use strict;
 
+use Test::More;
+BEGIN {
+    if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+        plan tests => 58;
+    }
+    else {
+        plan skip_all => "Net::OAuth isn't installed";
+    }
+}
+
 use lib 't/lib';
 use Jifty::SubTest;
 
 use TestApp::Plugin::OAuth::Test;
 
-if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
-    plan tests => 56;
-}
-else {
-    plan skip_all => "Net::OAuth isn't installed";
-}
-
-use Jifty::Test::WWW::Mechanize;
-
-my $server  = Jifty::Test->make_server;
-isa_ok($server, 'Jifty::Server');
-my $URL     = $server->started_ok;
-$mech    = Jifty::Test::WWW::Mechanize->new();
-$url     = $URL . '/oauth/request_token';
-
 # create some consumers {{{
 my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
 my ($ok, $msg) = $consumer->create(
@@ -162,17 +157,6 @@
     oauth_signature        => 'hello ^____^',
 );
 # }}}
-# duplicate timestamp and nonce {{{
-response_is(
-    code                   => 401,
-    testname               => "401 - duplicate timestamp and nonce",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_timestamp        => 1,
-    oauth_nonce            => 1,
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
 # unknown signature method {{{
 response_is(
     code                   => 400,
@@ -275,3 +259,20 @@
 );
 # }}}
 
+# duplicate timestamp and nonce {{{
+response_is(
+    code                   => 200,
+    testname               => "200 - plaintext signature",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+--$timestamp;
+response_is(
+    code                   => 401,
+    testname               => "401 - duplicate timestamp and nonce",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}

Modified: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/03-authorize.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/03-authorize.t	(original)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/03-authorize.t	Tue Nov 13 14:05:20 2007
@@ -2,26 +2,23 @@
 use warnings;
 use strict;
 
+use Test::More;
+BEGIN {
+    if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+        plan tests => 85;
+    }
+    else {
+        plan skip_all => "Net::OAuth isn't installed";
+    }
+}
+
 use lib 't/lib';
 use Jifty::SubTest;
 
 use TestApp::Plugin::OAuth::Test;
 
-if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
-    plan tests => 40;
-}
-else {
-    plan skip_all => "Net::OAuth isn't installed";
-}
-
 use Jifty::Test::WWW::Mechanize;
 
-my $server  = Jifty::Test->make_server;
-isa_ok($server, 'Jifty::Server');
-my $URL     = $server->started_ok;
-$mech    = Jifty::Test::WWW::Mechanize->new();
-$url     = $URL . '/oauth/request_token';
-
 # create some consumers {{{
 my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
 my ($ok, $msg) = $consumer->create(
@@ -42,39 +39,36 @@
 );
 ok($ok, $msg);
 # }}}
-# get a request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
+
 # try to navigate to protected pages while not logged in {{{
-$mech->get_ok('/oauth/authorize');
-$mech->content_unlike(qr/If you trust this application/);
+$umech->get_ok($URL . '/oauth/authorize');
+$umech->content_unlike(qr/If you trust this application/);
+
+$umech->get_ok('/oauth/authorized');
+$umech->content_unlike(qr/If you trust this application/);
 
-$mech->get_ok('/nuke/the/whales');
-$mech->content_unlike(qr/Press the shiny red button/);
+$umech->get_ok('/nuke/the/whales');
+$umech->content_unlike(qr/Press the shiny red button/);
 # }}}
 # log in {{{
 my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser);
 $u->create( name => 'You Zer', email => 'youzer at example.com', password => 'secret', email_confirmed => 1);
 ok($u->id, "New user has valid id set");
 
-$mech->get_ok('/login');
-$mech->fill_in_action_ok($mech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer at example.com', password => 'secret');
-$mech->submit;
-$mech->save_content('m2.html');
-$mech->content_contains('Logout');
+$umech->get_ok('/login');
+$umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer at example.com', password => 'secret');
+$umech->submit;
+$umech->content_contains('Logout');
 # }}}
 # try to navigate to protected pages while logged in {{{
-$mech->get_ok('/oauth/authorize');
-$mech->content_like(qr/If you trust this application/);
+$umech->get_ok('/oauth/authorize');
+$umech->content_like(qr/If you trust this application/);
 
-$mech->get_ok('/nuke/the/whales');
-$mech->content_like(qr/Press the shiny red button/);
+$umech->get_ok('/oauth/authorized');
+$umech->content_like(qr/If you trust this application/);
+
+$umech->get_ok('/nuke/the/whales');
+$umech->content_like(qr/Press the shiny red button/);
 # }}}
 # deny an unknown access token {{{
 my $error = _authorize_request_token('Deny', 'deadbeef');
@@ -82,7 +76,7 @@
     ok(0, $error);
 }
 else {
-    $mech->content_contains("I don't know of that request token.");
+    $umech->content_contains("I don't know of that request token.");
 }
 # }}}
 # allow an unknown access token {{{
@@ -91,10 +85,11 @@
     ok(0, $error);
 }
 else {
-    $mech->content_contains("I don't know of that request token.");
+    $umech->content_contains("I don't know of that request token.");
 }
 # }}}
-# deny the above request token {{{
+# deny request token {{{
+get_request_token();
 deny_ok();
 # }}}
 # try to use the denied request token {{{
@@ -103,19 +98,11 @@
     ok(0, $error);
 }
 else {
-    $mech->content_contains("I don't know of that request token.");
+    $umech->content_contains("I don't know of that request token.");
 }
 # }}}
-# get another request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
-# allow the above request token {{{
+# allow request token {{{
+get_request_token();
 allow_ok();
 # }}}
 # try to allow again {{{
@@ -124,19 +111,12 @@
     ok(0, $error);
 }
 else {
-    $mech->content_contains("I don't know of that request token.");
+    $umech->content_contains("I don't know of that request token.");
 }
 # }}}
-# get another request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
-# expire the token, try to allow it {{{
+# expire a token, try to allow it {{{
+get_request_token();
+
 my $late = Jifty::DateTime->now(time_zone => 'GMT')->subtract(minutes => 10);
 $token_obj->set_valid_until($late);
 
@@ -145,7 +125,7 @@
     ok(0, $error);
 }
 else {
-    $mech->content_contains("This request token has expired.");
+    $umech->content_contains("This request token has expired.");
 }
 # }}}
 # try again, it should be deleted {{{
@@ -154,89 +134,92 @@
     ok(0, $error);
 }
 else {
-    $mech->content_contains("I don't know of that request token.");
+    $umech->content_contains("I don't know of that request token.");
 }
 # }}}
 
-# get another request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
-# deny it with a request parameter {{{
-$mech->get_ok('/oauth/authorize?oauth_token=' . $token_obj->token);
-$mech->content_like(qr/If you trust this application/);
-$mech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it");
-
-$mech->form_number(1);
-$mech->click_button(value => 'Deny');
-
-$mech->content_contains("Denying FooBar Industries the right to access your stuff");
-# }}}
-# get another request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
-# allow it with a request parameter {{{
-$mech->get_ok('/oauth/authorize?oauth_token=' . $token_obj->token);
-$mech->content_like(qr/If you trust this application/);
-$mech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it");
-
-$mech->form_number(1);
-$mech->click_button(value => 'Allow');
-
-$mech->content_contains("Allowing FooBar Industries to access your stuff");
-# }}}
-# get another request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
-# }}}
-# deny it with a callback {{{
-$mech->get_ok('/oauth/authorize?oauth_callback=http%3A%2f%2fgoogle.com');
-$mech->content_like(qr/If you trust this application/);
-
-$mech->fill_in_action_ok($mech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken'), token => $token_obj->token);
-$mech->click_button(value => 'Deny');
-
-$mech->content_contains("Denying FooBar Industries the right to access your stuff");
-$mech->content_contains("Click here");
-$mech->content_contains("http://google.com?oauth_token=" . $token_obj->token);
-$mech->content_contains("to return to FooBar Industries");
-# }}}
-# get another request token as a known consumer (PLAINTEXT) {{{
-response_is(
-    code                   => 200,
-    testname               => "200 - plaintext signature",
-    consumer_secret        => 'bar',
-    oauth_consumer_key     => 'foo',
-    oauth_signature_method => 'PLAINTEXT',
-);
+# deny token with a request parameter {{{
+get_request_token();
+$umech->get_ok('/oauth/authorize?oauth_token=' . $token_obj->token);
+$umech->content_like(qr/If you trust this application/);
+$umech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it");
+
+$umech->form_number(1);
+$umech->click_button(value => 'Deny');
+
+$umech->content_contains("Denying FooBar Industries the right to access your stuff");
+$umech->content_contains("click here");
+$umech->content_contains("http://foo.bar.example.com?oauth_token=" . $token_obj->token);
+$umech->content_contains("To return to");
+$umech->content_contains("FooBar Industries");
+# }}}
+# allow token with a request parameter {{{
+get_request_token();
+$umech->get_ok('/oauth/authorize?oauth_token=' . $token_obj->token);
+$umech->content_like(qr/If you trust this application/);
+$umech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it");
+
+$umech->form_number(1);
+$umech->click_button(value => 'Allow');
+
+$umech->content_contains("Allowing FooBar Industries to access your stuff");
+$umech->content_contains("click here");
+$umech->content_contains("http://foo.bar.example.com?oauth_token=" . $token_obj->token);
+$umech->content_contains("To return to");
+$umech->content_contains("FooBar Industries");
+# }}}
+# deny token with a callback {{{
+get_request_token();
+$umech->get_ok('/oauth/authorize?oauth_callback=http%3A%2f%2fgoogle.com');
+$umech->content_like(qr/If you trust this application/);
+
+$umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken'), token => $token_obj->token);
+$umech->click_button(value => 'Deny');
+
+$umech->content_contains("Denying FooBar Industries the right to access your stuff");
+$umech->content_contains("click here");
+$umech->content_contains("http://google.com?oauth_token=" . $token_obj->token);
+$umech->content_contains("To return to");
+$umech->content_contains("FooBar Industries");
 # }}}
 # deny it with a callback + request params {{{
-$mech->get_ok('/oauth/authorize?oauth_token='.$token_obj->token.'&oauth_callback=http%3A%2f%2fgoogle.com%3ffoo%3d=bar');
-$mech->content_like(qr/If you trust this application/);
-$mech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it");
-
-$mech->form_number(1);
-$mech->click_button(value => 'Deny');
-
-$mech->content_contains("Denying FooBar Industries the right to access your stuff");
-$mech->content_contains("Click here");
-$mech->content_contains("http://google.com?foo=bar&oauth_token=" . $token_obj->token);
-$mech->content_contains("to return to FooBar Industries");
+get_request_token();
+$umech->get_ok('/oauth/authorize?oauth_token='.$token_obj->token.'&oauth_callback=http%3A%2F%2Fgoogle.com%2F%3Ffoo%3Dbar');
+$umech->content_like(qr/If you trust this application/);
+$umech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it");
+
+$umech->form_number(1);
+$umech->click_button(value => 'Deny');
+
+$umech->content_contains("Denying FooBar Industries the right to access your stuff");
+$umech->content_contains("click here");
+my $token = $token_obj->token;
+$umech->content_like(qr{http://google\.com/\?foo=bar&(?:amp;|#38;)?oauth_token=$token});
+$umech->content_contains("To return to");
+$umech->content_contains("FooBar Industries");
+# }}}
+
+# authorizing a token refreshes its valid_until {{{
+get_request_token();
+my $in_ten = DateTime->now(time_zone => "GMT")->add(minutes => 10);
+$token_obj->set_valid_until($in_ten->clone);
+
+my $id = $token_obj->id;
+undef $token_obj;
+$token_obj = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
+$token_obj->load($id);
+
+allow_ok();
+
+undef $token_obj;
+$token_obj = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
+$token_obj->load($id);
+
+my $difference = $token_obj->valid_until - $in_ten;
+
+TODO: {
+    local $TODO = "some kind of caching issue, serverside it works fine";
+    ok($difference->minutes > 15, "valid for more than 15 minutes");
+}
 # }}}
 

Added: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/04-access-token.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/04-access-token.t	Tue Nov 13 14:05:20 2007
@@ -0,0 +1,186 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+use Test::More;
+BEGIN {
+    if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+        plan tests => 70;
+    }
+    else {
+        plan skip_all => "Net::OAuth isn't installed";
+    }
+}
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use TestApp::Plugin::OAuth::Test;
+
+use Jifty::Test::WWW::Mechanize;
+
+# setup {{{
+# create two consumers {{{
+my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
+my ($ok, $msg) = $consumer->create(
+    consumer_key => 'foo',
+    secret       => 'bar',
+    name         => 'FooBar Industries',
+    url          => 'http://foo.bar.example.com',
+    rsa_key      => $pubkey,
+);
+ok($ok, $msg);
+
+my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
+($ok, $msg) = $rsaless->create(
+    consumer_key => 'foo2',
+    secret       => 'bar2',
+    name         => 'Backwater.org',
+    url          => 'http://backwater.org',
+);
+ok($ok, $msg);
+# }}}
+# create user and log in {{{
+my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser);
+$u->create( name => 'You Zer', email => 'youzer at example.com', password => 'secret', email_confirmed => 1);
+ok($u->id, "New user has valid id set");
+
+$umech->get_ok($URL . '/login');
+$umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer at example.com', password => 'secret');
+$umech->submit;
+$umech->content_contains('Logout');
+# }}}
+# }}}
+# basic working access token {{{
+get_authorized_token();
+my $request_token = $token_obj->token;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 200,
+    testname               => "200 - plaintext signature",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+isnt($token_obj->token, $request_token, "different token for request and access");
+# }}}
+# try to get an access token from denied request token {{{
+get_request_token();
+deny_ok();
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - denied token",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+# try to get an access token as a different consumer {{{
+get_authorized_token();
+$request_token = $token_obj;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - denied token",
+    consumer_secret        => 'bar2',
+    oauth_consumer_key     => 'foo2',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+# get that same access token as the original consumer {{{
+$token_obj = $request_token;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 200,
+    testname               => "200 - got token",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+# same timestamp, different nonce {{{
+get_authorized_token();
+--$timestamp;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 200,
+    testname               => "200 - plaintext signature",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+    oauth_nonce            => 'kjfh',
+);
+# }}}
+# different timestamp, same nonce {{{
+get_authorized_token();
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 200,
+    testname               => "200 - plaintext signature",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+    oauth_nonce            => 'kjfh',
+);
+# }}}
+# duplicate timestamp and nonce as previous access token {{{
+get_authorized_token();
+$timestamp -= 2;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - duplicate ts/nonce as previous access",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+$timestamp += 100;
+# }}}
+# duplicate timestamp and nonce as request token {{{
+get_authorized_token();
+--$timestamp;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - duplicate ts/nonce for request token",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+# same request token {{{
+$token_obj = $request_token;
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - already used",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+# expired request token {{{
+get_authorized_token();
+$token_obj->set_valid_until(DateTime->now(time_zone => "GMT")->subtract(days => 1));
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - expired",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+# wrong consumer secret {{{
+get_authorized_token();
+response_is(
+    url                    => '/oauth/access_token',
+    code                   => 401,
+    testname               => "401 - wrong secret",
+    consumer_secret        => 'bah!',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+# }}}
+

Added: jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/05-protected-resource.t
==============================================================================
--- (empty file)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-OAuth/t/05-protected-resource.t	Tue Nov 13 14:05:20 2007
@@ -0,0 +1,66 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+use Test::More;
+BEGIN {
+    if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+        plan tests => 16;
+    }
+    else {
+        plan skip_all => "Net::OAuth isn't installed";
+    }
+}
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use TestApp::Plugin::OAuth::Test;
+
+use Jifty::Test::WWW::Mechanize;
+
+# setup {{{
+# create two consumers {{{
+my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
+my ($ok, $msg) = $consumer->create(
+    consumer_key => 'foo',
+    secret       => 'bar',
+    name         => 'FooBar Industries',
+    url          => 'http://foo.bar.example.com',
+    rsa_key      => $pubkey,
+);
+ok($ok, $msg);
+
+my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
+($ok, $msg) = $rsaless->create(
+    consumer_key => 'foo2',
+    secret       => 'bar2',
+    name         => 'Backwater.org',
+    url          => 'http://backwater.org',
+);
+ok($ok, $msg);
+# }}}
+# create user and log in {{{
+my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser);
+$u->create( name => 'You Zer', email => 'youzer at example.com', password => 'secret', email_confirmed => 1);
+ok($u->id, "New user has valid id set");
+
+$umech->get_ok($URL . '/login');
+$umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer at example.com', password => 'secret');
+$umech->submit;
+$umech->content_contains('Logout');
+# }}}
+# }}}
+# basic protected request {{{
+get_access_token();
+response_is(
+    url                    => '/nuke/the/whales',
+    code                   => 200,
+    testname               => "200 - protected resource request",
+    consumer_secret        => 'bar',
+    oauth_consumer_key     => 'foo',
+    oauth_signature_method => 'PLAINTEXT',
+);
+$cmech->content_contains("Press the shiny red button", "got to a protected page");
+# }}}
+

Modified: jifty/branches/virtual-models/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm
==============================================================================
--- jifty/branches/virtual-models/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm	(original)
+++ jifty/branches/virtual-models/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm	Tue Nov 13 14:05:20 2007
@@ -25,6 +25,9 @@
 					     arguments => { name => $foo });
 	my $redir = new_action(class     => "Jifty::Action::Redirect",
 			       arguments => { url => '/index.html' });
+	render_region( 'foo' );
+	hyperlink( label => 'foo', onclick => { region => 'foo', replace_with => '_r_foo', args => { foo => '123123' } } );
+
 	form {
 	    Jifty->web->form->register_action($redir);
 	    render_action($create);
@@ -41,5 +44,10 @@
 	}
     };
 
+template '_r_foo' => sub {
+    my $foo = get('foo');
+    h1 { $foo };
+};
+
 1;
 

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 Nov 13 14:05:20 2007
@@ -11,7 +11,7 @@
 use lib 't/lib';
 use Jifty::SubTest;
 
-use Jifty::Test tests => 27;
+use Jifty::Test tests => 33;
 use Jifty::Test::WWW::Mechanize;
 
 use_ok('TestApp::Model::User');
@@ -37,6 +37,12 @@
 like($o->created_on->time_zone, qr/Floating/, "User's created_on date is in the floating timezone");
 like($o->current_time->time_zone, qr/UTC/, "Jifty::DateTime::now defaults to UTC (superuser has no user_object)");
 
+my $now = $o->current_time->clone;
+$now->set_current_user_timezone('America/Chicago');
+like($now->time_zone, , qr{America::Chicago}, "set_current_user_timezone defaults to the passed in timezone");
+$now->set_current_user_timezone();
+like($now->time_zone, , qr{UTC}, "set_current_user_timezone defaults to UTC if no passed in timezone");
+
 is($o->email, 'bob at example.com', 'email initially set correctly');
 $o->set_email('bob+jifty at example.com');
 is($o->email, 'bob+jifty at example.com', 'email updated correctly');
@@ -54,6 +60,17 @@
 like($bob->user_object->created_on->time_zone, qr/Floating/, "User's created_on date is in the floating timezone");
 like($bob->user_object->current_time->time_zone, qr{America::Anchorage}, "Jifty::DateTime::now correctly peers into current_user->user_object->time_zone");
 
+$now = $bob->user_object->current_time->clone;
+$now->set_time_zone('America/New_York');
+like($now->time_zone, qr{America::New_York}, "setting up other tests");
+$now->set_current_user_timezone();
+like($now->time_zone, qr{America::Anchorage}, "set_current_user_timezone correctly gets the user's timezone");
+$now->set_current_user_timezone('America/Chicago');
+like($now->time_zone, , qr{America::Anchorage}, "set_current_user_timezone uses the user's in timezone even if one is passed in");
+
+my $dt = Jifty::DateTime->from_epoch(epoch => time);
+like($now->time_zone, qr{America::Anchorage}, "from_epoch correctly gets the user's timezone");
+
 my $server = Jifty::Test->make_server;
 isa_ok($server, 'Jifty::Server');
 

Modified: jifty/branches/virtual-models/t/TestApp/t/18-test-www-declare.t
==============================================================================
--- jifty/branches/virtual-models/t/TestApp/t/18-test-www-declare.t	(original)
+++ jifty/branches/virtual-models/t/TestApp/t/18-test-www-declare.t	Tue Nov 13 14:05:20 2007
@@ -4,6 +4,13 @@
 
 use lib 't/lib';
 use Jifty::SubTest;
+use Test::More;
+
+BEGIN {
+    unless (eval { require Test::WWW::Declare }) {
+        plan skip_all => "Test::WWW::Declare isn't installed";
+    }
+}
 
 use Jifty::Test::WWW::Declare tests => 2;
 


More information about the Jifty-commit mailing list