[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