[Jifty-commit] r4420 - in jifty/branches/prototype-1.6: . bin contrib lib/Jifty lib/Jifty/Action lib/Jifty/Param lib/Jifty/Plugin lib/Jifty/Plugin/Authentication/Password/Mixin/Model lib/Jifty/Plugin/I18N/Action lib/Jifty/Plugin/OAuth lib/Jifty/Plugin/OAuth/Action lib/Jifty/Plugin/OAuth/Model lib/Jifty/Test/WWW 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
Mon Nov 12 08:57:12 EST 2007


Author: clkao
Date: Mon Nov 12 08:57:09 2007
New Revision: 4420

Added:
   jifty/branches/prototype-1.6/bin/sort-changelog   (contents, props changed)
   jifty/branches/prototype-1.6/contrib/
   jifty/branches/prototype-1.6/contrib/jifty_completion.sh
   jifty/branches/prototype-1.6/lib/Jifty/Manual.pm
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/View.pm
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/04-access-token.t
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/05-protected-resource.t
Modified:
   jifty/branches/prototype-1.6/   (props changed)
   jifty/branches/prototype-1.6/AUTHORS
   jifty/branches/prototype-1.6/Makefile.PL
   jifty/branches/prototype-1.6/bin/generate-changelog
   jifty/branches/prototype-1.6/lib/Jifty/Action/Record.pm
   jifty/branches/prototype-1.6/lib/Jifty/DateTime.pm
   jifty/branches/prototype-1.6/lib/Jifty/Handle.pm
   jifty/branches/prototype-1.6/lib/Jifty/I18N.pm
   jifty/branches/prototype-1.6/lib/Jifty/Param/Schema.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference/Widget.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N/Action/SetLang.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Dispatcher.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/View.pm
   jifty/branches/prototype-1.6/lib/Jifty/Plugin/SinglePage.pm
   jifty/branches/prototype-1.6/lib/Jifty/Test.pm
   jifty/branches/prototype-1.6/lib/Jifty/Test/WWW/Mechanize.pm
   jifty/branches/prototype-1.6/lib/Jifty/Test/WWW/Selenium.pm
   jifty/branches/prototype-1.6/lib/Jifty/Web/Form/Clickable.pm
   jifty/branches/prototype-1.6/lib/Jifty/Web/Form/Element.pm
   jifty/branches/prototype-1.6/lib/Jifty/Web/PageRegion.pm
   jifty/branches/prototype-1.6/t/99-pod-coverage.t
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/00-test-setup.t
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/01-basic.t
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/02-request-token.t
   jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/03-authorize.t
   jifty/branches/prototype-1.6/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm
   jifty/branches/prototype-1.6/t/TestApp/t/11-current_user.t
   jifty/branches/prototype-1.6/t/TestApp/t/18-test-www-declare.t

Log:
merge down for prototype-1.6 branch.

Modified: jifty/branches/prototype-1.6/AUTHORS
==============================================================================
--- jifty/branches/prototype-1.6/AUTHORS	(original)
+++ jifty/branches/prototype-1.6/AUTHORS	Mon Nov 12 08:57:09 2007
@@ -19,7 +19,7 @@
 Paul Fenwick <pjf at perltraining.com.au>
 Edmund von der Burg <evdb at ecclestoad.co.uk>
 Yves Agostini <agostini at univ-metz.fr>
-Agent Zhang <agentzh at gmail.com>
+Agent Zhang <agentzh at agentzh.org>
 Pawel Murias <pmurias at woobling.org>
 Kevin Falcone <falcone at bestpractical.com>
 Andrew Sterling Hanenkamp <sterling at hanenkamp.com>
@@ -32,3 +32,4 @@
 sunnavy <sunnavy at gmail.com>
 Shawn M Moore <sartak at gmail.com>
 Edward Funnekotter <efunneko at gmail.com>
+Cornelius Lin <c9s at aiink.com>

Modified: jifty/branches/prototype-1.6/Makefile.PL
==============================================================================
--- jifty/branches/prototype-1.6/Makefile.PL	(original)
+++ jifty/branches/prototype-1.6/Makefile.PL	Mon Nov 12 08:57:09 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');
@@ -73,7 +74,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 ),
@@ -112,6 +112,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/prototype-1.6/bin/generate-changelog
==============================================================================
--- jifty/branches/prototype-1.6/bin/generate-changelog	(original)
+++ jifty/branches/prototype-1.6/bin/generate-changelog	Mon Nov 12 08:57:09 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/prototype-1.6/bin/sort-changelog
==============================================================================
--- (empty file)
+++ jifty/branches/prototype-1.6/bin/sort-changelog	Mon Nov 12 08:57:09 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/prototype-1.6/contrib/jifty_completion.sh
==============================================================================
--- (empty file)
+++ jifty/branches/prototype-1.6/contrib/jifty_completion.sh	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Action/Record.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Action/Record.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Action/Record.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/DateTime.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/DateTime.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/DateTime.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Handle.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Handle.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Handle.pm	Mon Nov 12 08:57:09 2007
@@ -91,7 +91,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);
@@ -145,10 +145,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/prototype-1.6/lib/Jifty/I18N.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/I18N.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/I18N.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Manual.pm
==============================================================================
--- (empty file)
+++ jifty/branches/prototype-1.6/lib/Jifty/Manual.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Param/Schema.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Param/Schema.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Param/Schema.pm	Mon Nov 12 08:57:09 2007
@@ -172,10 +172,30 @@
 =cut
 
 sub merge_params {
+    # 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.  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(@_);
     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/prototype-1.6/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm	Mon Nov 12 08:57:09 2007
@@ -153,8 +153,6 @@
     $self->regenerate_auth_token;
     if ( $self->id and $self->email and not $self->email_confirmed ) {
         Jifty->app_class('Notification','ConfirmEmail')->new( to => $self )->send;
-    } else {
-        warn  $self->id . " " .$self->email;
     }
 }
 

Modified: jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference.pm	Mon Nov 12 08:57:09 2007
@@ -31,6 +31,12 @@
 
 Provides a special autocompletion widget for reference columns. See L<Jifty::Plugin::AutoReference::Widget>.
 
+=head1 METHODS
+
+=head2 init
+
+Adds the F<autoreference.js> file to the JavaScript files to send to the browser.
+
 =cut
 
 sub init {

Modified: jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference/Widget.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference/Widget.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/AutoReference/Widget.pm	Mon Nov 12 08:57:09 2007
@@ -24,6 +24,12 @@
 
 B<WARNING:> As of this writing, it should be noted that this widget does not degrade gracefully. If you need a widget that operates properly even when JavaScript is unavailable, this widget won't do that job at this time.
 
+=head1 METHODS
+
+=head2 render
+
+Overrides the field renderer to force autocompletion to be turned on.
+
 =cut
 
 sub render {
@@ -40,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';
@@ -74,6 +84,12 @@
     }
 }
 
+=head2 render_widget
+
+Overrides the widget renderer to draw both a hidden field that stores the actual referenced ID and a text field that is autocompleted using the records brief description.
+
+=cut
+
 sub render_widget {
     my $self = shift;
 
@@ -99,6 +115,12 @@
     return '';
 }
 
+=head2 render_value
+
+Overrides the value renderer to show the brief description of the referenced record.
+
+=cut
+
 sub render_value {
     my $self = shift;
 
@@ -109,6 +131,12 @@
     return '';
 }
 
+=head2 autocomplete_javascript
+
+Overrides the JavaScript autocompletion widget to use C<Jifty.Plugin.AutoReference> instead of the regular C<Jifty.Autocompleter>.
+
+=cut
+
 sub autocomplete_javascript {
     my $self = shift;
     return qq{new Jifty.Plugin.AutoReference('@{[$self->element_id]}-display','@{[$self->element_id]}','@{[$self->element_id]}-autocomplete')};

Modified: jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Plugin/I18N/Action/SetLang.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N/Action/SetLang.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/I18N/Action/SetLang.pm	Mon Nov 12 08:57:09 2007
@@ -2,17 +2,38 @@
 use strict;
 use DateTime::Locale ();
 
+use base 'Class::Data::Inheritable';
+__PACKAGE__->mk_classdata(_available_languages => undef);
+
 use Jifty::Param::Schema;
 use Jifty::Action schema {
 
-param lang =>
-    label is _('Language'),
+param
+    lang => label is _('Language'),
     render as 'select',
-    # XXX: complete_native_name is way too long
-    valid are defer {[ map { { display => DateTime::Locale->load($_)->native_name, value => $_ } } Jifty::I18N->available_languages ]},
     default is defer { Jifty::I18N->get_current_language };
 };
 
+sub available_languages {
+    my $class = shift;
+    return $class->_available_languages if $class->_available_languages;
+
+    $class->_available_languages(
+        [   map { {   display => DateTime::Locale->load($_)->native_name,
+                      value   => $_ } } Jifty::I18N->available_languages ] );
+    return $class->_available_languages;
+}
+
+sub arguments {
+    my $self = shift;
+    my $args = $self->SUPER::arguments;
+
+    # XXX: complete_native_name is way too long
+    $args->{lang}->{valid_values} = $self->available_languages;
+
+    return $args;
+}
+
 sub take_action {
     my $self = shift;
     my $lang = $self->argument_value('lang');

Modified: jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm	Mon Nov 12 08:57:09 2007
@@ -21,6 +21,9 @@
     param 'authorize',
         valid_values are qw(allow deny);
 
+    param 'callback',
+        render as 'hidden';
+
 };
 
 =head2 validate_token
@@ -36,11 +39,16 @@
     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;
 
+    if ($request_token->valid_until < Jifty::DateTime->now(time_zone => 'GMT')) {
+        $request_token->delete();
+        return $self->validation_error(token => "This request token has expired.");
+    }
+
     return $self->validation_ok('token');
 }
 
@@ -58,6 +66,10 @@
         token => $self->argument_value('token'),
     );
 
+    $self->result->content(token_obj => $token);
+    $self->result->content(token     => $token->token);
+    $self->result->content(callback  => $self->argument_value('callback'));
+
     if ($self->argument_value('authorize') eq 'allow') {
         $token->set_authorized('t');
         $self->result->message("Allowing " . $token->consumer->name . " to access your stuff.");

Modified: jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Dispatcher.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Dispatcher.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Dispatcher.pm	Mon Nov 12 08:57:09 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
@@ -94,13 +98,13 @@
     set no_abort => 1;
     my %oauth_params = get_parameters(@params);
 
-    set next => $oauth_params{callback};
+    set callback => $oauth_params{callback};
     set consumer => 'Some application';
     del 'token';
 
     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/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/Consumer.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/Consumer.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Plugin/OAuth/View.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/View.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/OAuth/View.pm	Mon Nov 12 08:57:09 2007
@@ -103,7 +103,7 @@
         class   => 'AuthorizeRequestToken',
     );
 
-    Jifty->web->form->start( call => get 'next' );
+    Jifty->web->form->start();
 
     # if the site put the token in the request, then use it
     # otherwise, prompt the user for it
@@ -116,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' },
@@ -129,6 +131,44 @@
     Jifty->web->form->end();
 };
 
+=head2 oauth/authorized
+
+Displayed after the user authorizes or denies a request token. Uses a link
+to the callback if provided, otherwise the site's URL.
+
+=cut
+
+template 'oauth/authorized' => page { title => 'XXX' }
+content {
+    my $result    = get 'result';
+    my $callback  = $result->content('callback');
+    my $token     = $result->content('token');
+    my $token_obj = $result->content('token_obj');
+
+    $callback ||= $token_obj->consumer->url;
+
+    if (!$callback) {
+        p { "Oops! " . $token_obj->consumer->name . " didn't tell us how to get you back to their service. If you do find your way back, you'll probably need this token: " . $token };
+    }
+    else {
+        $callback .= ($callback =~ /\?/ ? '&' : '?')
+                  .  'oauth_token='
+                  .  $token;
+        set consumer => $token_obj->consumer;
+
+        p {
+            outs 'To return to ';
+            show '/oauth/consumer';
+            outs ', ';
+            hyperlink(
+                label => 'click here',
+                url   => $callback,
+            );
+            outs '.';
+        };
+    }
+};
+
 =head2 oauth/help
 
 This provides a very, very layman description of OAuth for users

Modified: jifty/branches/prototype-1.6/lib/Jifty/Plugin/SinglePage.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Plugin/SinglePage.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Plugin/SinglePage.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Test.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Test.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Test.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Test/WWW/Mechanize.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Test/WWW/Mechanize.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Test/WWW/Mechanize.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Test/WWW/Selenium.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Test/WWW/Selenium.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Test/WWW/Selenium.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Web/Form/Clickable.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Web/Form/Clickable.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Web/Form/Clickable.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Web/Form/Element.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Web/Form/Element.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Web/Form/Element.pm	Mon Nov 12 08:57:09 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/prototype-1.6/lib/Jifty/Web/PageRegion.pm
==============================================================================
--- jifty/branches/prototype-1.6/lib/Jifty/Web/PageRegion.pm	(original)
+++ jifty/branches/prototype-1.6/lib/Jifty/Web/PageRegion.pm	Mon Nov 12 08:57:09 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/prototype-1.6/t/99-pod-coverage.t
==============================================================================
--- jifty/branches/prototype-1.6/t/99-pod-coverage.t	(original)
+++ jifty/branches/prototype-1.6/t/99-pod-coverage.t	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm	Mon Nov 12 08:57:09 2007
@@ -5,19 +5,20 @@
 
 my @login_required = qw{
     oauth/authorize
+    nuke/?
 };
 
 my $login_required = join '|', map {"^$_"} @login_required;
 $login_required = qr/$login_required/;
 
-before '*' => run {
+before '**' => run {
     if (Jifty->web->current_user->id) {
         my $top = Jifty->web->navigation;
         $top->child( _('Pick!')    => url => '/pick' );
         $top->child( _('Choices')  => url => '/choices' );
     }
     elsif ($1 =~ $login_required) {
-        tangent 'login';
+        tangent '/login';
     }
 };
 

Modified: jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm	Mon Nov 12 08:57:09 2007
@@ -7,20 +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 response_is sign get_latest_token);
+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 $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);
-}
 
-our $timestamp = 0;
-our $url;
-our $mech;
-our $pubkey = slurp('t/id_rsa.pub');
-our $seckey = slurp('t/id_rsa');
+    $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;
@@ -38,6 +52,9 @@
         @_,
     );
 
+    local $url = $URL . delete $params{url}
+        if $params{url};
+
     for (grep {!defined $params{$_}} keys %params) {
         delete $params{$_};
     }
@@ -49,36 +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);
 
-    my $token = get_latest_token();
-    if ($code == 200) {
-        main::ok($token, "Successfully loaded a token object with token ".$token->token.".");
-    }
-    else {
-        main::ok(!$token, "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};
@@ -140,7 +167,7 @@
 }
 
 sub get_latest_token {
-    my $content = $mech->content;
+    my $content = $cmech->content;
 
     $content =~ s/\boauth_token=(\w+)//
         or return;
@@ -155,18 +182,18 @@
 
     my $package = 'Jifty::Plugin::OAuth::Model::';
 
-    if ($mech->uri =~ /request_token/) {
+    if ($cmech->uri =~ /request_token/) {
         $package .= 'RequestToken';
     }
-    elsif ($mech->uri =~ /request_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;
     }
 
-    my $token_obj = $package->new(current_user => Jifty::CurrentUser->superuser);
+    $token_obj = $package->new(current_user => Jifty::CurrentUser->superuser);
     $token_obj->load_by_cols(token => $token);
 
     if (!$token_obj->id) {
@@ -177,5 +204,82 @@
     return $token_obj;
 }
 
+sub allow_ok {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    my $error = _authorize_request_token('Allow');
+    ok(0, $error), return if $error;
+
+    my $name = $token_obj->consumer->name;
+    $umech->content_contains("Allowing $name to access your stuff");
+}
+
+sub deny_ok {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    my $error = _authorize_request_token('Deny');
+    ok(0, $error), return if $error;
+
+    my $name = $token_obj->consumer->name;
+    $umech->content_contains("Denying $name the right to access your stuff");
+}
+
+sub _authorize_request_token {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    my $which_button = shift
+        or die "You did not specify a button to click to _authorize_request_token";
+
+    my $token = shift || $token_obj->token;
+    $token = $token->token if ref $token;
+
+    $umech->get('/oauth/authorize')
+        or return "Unable to navigate to /oauth/authorize";;
+    $umech->content =~ /If you trust this application/
+        or return "Content did not much qr/If you trust this application/";
+    my $moniker = $umech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken')
+        or return "Unable to find moniker for AuthorizeRequestToken";
+    $umech->fill_in_action($moniker, token => $token)
+        or return "Unable to fill in the AuthorizeRequestToken action";
+    $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/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/View.pm	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp-Plugin-OAuth/t/00-test-setup.t
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/00-test-setup.t	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/00-test-setup.t	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp-Plugin-OAuth/t/01-basic.t
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/01-basic.t	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/01-basic.t	Mon Nov 12 08:57:09 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;
@@ -29,5 +32,5 @@
 $mech->content_like(qr{http://oauth\.net/}, "oauth page mentions OAuth homepage");
 
 $mech->get_ok($URL . '/oauth/authorize');
-$mech->content_like(qr{If you trust this application}, "oauth authorization page exists without fancy headers");
+$mech->content_unlike(qr{If you trust this application}, "/oauth/authorize requires being logged in");
 

Modified: jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/02-request-token.t
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/02-request-token.t	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/02-request-token.t	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp-Plugin-OAuth/t/03-authorize.t
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/03-authorize.t	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/03-authorize.t	Mon Nov 12 08:57:09 2007
@@ -2,32 +2,29 @@
 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 => 9;
-}
-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(
     consumer_key => 'foo',
     secret       => 'bar',
-    name         => 'FooBar industries',
+    name         => 'FooBar Industries',
     url          => 'http://foo.bar.example.com',
     rsa_key      => $pubkey,
 );
@@ -42,13 +39,187 @@
 );
 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 {{{
+$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/);
+
+$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");
+
+$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 {{{
+$umech->get_ok('/oauth/authorize');
+$umech->content_like(qr/If you trust this application/);
+
+$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');
+if ($error) {
+    ok(0, $error);
+}
+else {
+    $umech->content_contains("I don't know of that request token.");
+}
+# }}}
+# allow an unknown access token {{{
+$error = _authorize_request_token('Allow', 'hamburger');
+if ($error) {
+    ok(0, $error);
+}
+else {
+    $umech->content_contains("I don't know of that request token.");
+}
+# }}}
+# deny request token {{{
+get_request_token();
+deny_ok();
+# }}}
+# try to use the denied request token {{{
+$error = _authorize_request_token('Deny');
+if ($error) {
+    ok(0, $error);
+}
+else {
+    $umech->content_contains("I don't know of that request token.");
+}
+# }}}
+# allow request token {{{
+get_request_token();
+allow_ok();
+# }}}
+# try to allow again {{{
+$error = _authorize_request_token('Allow');
+if ($error) {
+    ok(0, $error);
+}
+else {
+    $umech->content_contains("I don't know of that request token.");
+}
+# }}}
+# 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);
+
+$error = _authorize_request_token('Allow');
+if ($error) {
+    ok(0, $error);
+}
+else {
+    $umech->content_contains("This request token has expired.");
+}
+# }}}
+# try again, it should be deleted {{{
+$error = _authorize_request_token('Allow');
+if ($error) {
+    ok(0, $error);
+}
+else {
+    $umech->content_contains("I don't know of that request token.");
+}
+# }}}
+
+# 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 {{{
+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/prototype-1.6/t/TestApp-Plugin-OAuth/t/04-access-token.t
==============================================================================
--- (empty file)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/04-access-token.t	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp-Plugin-OAuth/t/05-protected-resource.t
==============================================================================
--- (empty file)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-OAuth/t/05-protected-resource.t	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm	(original)
+++ jifty/branches/prototype-1.6/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp/t/11-current_user.t
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp/t/11-current_user.t	(original)
+++ jifty/branches/prototype-1.6/t/TestApp/t/11-current_user.t	Mon Nov 12 08:57:09 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/prototype-1.6/t/TestApp/t/18-test-www-declare.t
==============================================================================
--- jifty/branches/prototype-1.6/t/TestApp/t/18-test-www-declare.t	(original)
+++ jifty/branches/prototype-1.6/t/TestApp/t/18-test-www-declare.t	Mon Nov 12 08:57:09 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