Merge commit 'v7.10.18' into 8

Conflicts:
	docs/gotcha.txt
	docs/previousVersion.sql
	docs/templates.txt
	lib/WebGUI.pm
	lib/WebGUI/Asset/File.pm
	lib/WebGUI/Asset/Story.pm
	lib/WebGUI/Asset/Wobject/Calendar.pm
	lib/WebGUI/Asset/Wobject/Thingy.pm
	lib/WebGUI/AssetExportHtml.pm
	lib/WebGUI/Content/AssetManager.pm
	lib/WebGUI/Group.pm
	lib/WebGUI/Macro/AssetProxy.pm
	lib/WebGUI/Shop/PayDriver/PayPal/PayPalStd.pm
	lib/WebGUI/Storage.pm
	t/Asset/AssetExportHtml.t
	t/Asset/Story.t
	t/Shop/TaxDriver/Generic.t
	t/Storage.t
This commit is contained in:
Doug Bell 2011-06-21 16:03:49 -05:00
commit 0c5acb697b
75 changed files with 979 additions and 139 deletions

View file

@ -1,3 +1,20 @@
7.10.18
- fixed #12138: Version tag gets create by entering and direct leaving
- Added the WebGUI::Event API
- fixed #12141: Macro_RenderThingData (bad tags) nothing to translate
- fixed #12142: Copy fails on imported threads
- canView will now be checked before calling ANY www_ method on account
plugins to fix an Inbox security bug (and other similar potential bugs).
- fixed #12139: break on calander feeds during upgrade
- fixed #12136: Unable to add more than one image in Story
- fixed #12133: RenderThingData macro doesn't accept templateId
- fixed #12152: PayPal Standard ignores shop-credit
- fixed #12119: Locale setting for paypal
- fixed #12156: Asset Manager performance
- fixed #12158: Shop credit cannot be used to pay for Shipping on PayPal
- rfe #12159: Asset Manager sort preferences
- added: new default Collaboration System notification template.
7.10.17
- fixed: Forced to use a PayDriver even with a balance of 0 in the cart.
- add #12134: Access metadata for assets inside an AssetReport

View file

@ -21,9 +21,19 @@ save you many hours of grief.
Account Macro template
Admin Toggle Macro template
7.10.17
--------------------------------------------------------------------
* Due to a formatting problem with form variables in the PayPal driver, WebGUI
in-shop credit was not being recieved by PayPal. This means that Shop users
were charged the full amount of the cart instead of the discounted amount.
7.10.15
--------------------------------------------------------------------
* WebGUI now depends on Geo::Coder::Googlev3 for it's Map asset
* canView has to pass for any www_ methods to get called at all on account
plugins. This is the correct behavior, but is a change in this version.
Please review your custom account plugins to make sure you aren't
relying on the old buggy behavior.
7.10.13
--------------------------------------------------------------------

View file

@ -1,8 +1,6 @@
This is a running list of template changes made during upgrades. If you have copied the default
templates, you will need to apply these changes manually to your copies.
<<<<<<< HEAD
7.8.0
* Account Macro template variables renamed:
@ -13,6 +11,10 @@ templates, you will need to apply these changes manually to your copies.
toggle.url => toggle_url
toggle.text => toggle_text
7.10.18
* Collaboration System Default Notification Template /default_forum_notification
Replace table with divs to make inline replying easier.
7.10.15
* Gallery CSS - root/import/gallery-templates/gallery.css
Remove float:left on .wgAlbum.

View file

@ -0,0 +1,172 @@
#!/usr/bin/env perl
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
our ($webguiRoot);
BEGIN {
$webguiRoot = "../..";
unshift (@INC, $webguiRoot."/lib");
}
use strict;
use Getopt::Long;
use WebGUI::Session;
use WebGUI::Storage;
use WebGUI::Asset;
my $toVersion = '7.10.18';
my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
addAssetManagerSortPreferences($session);
finish($session); # this line required
#----------------------------------------------------------------------------
sub addAssetManagerSortPreferences {
my $cn = 'assetManagerSortColumn';
my $on = 'assetManagerSortDirection';
unless (WebGUI::ProfileField->new($session, $cn)) {
print 'Adding Asset Manager Sort Column profile field...'
unless $quiet;
WebGUI::ProfileField->create($session, $cn => {
label =>
"WebGUI::International::get('$cn label', 'Account_Profile')",
protected => 1,
fieldType => 'selectBox',
dataDefault => 'lineage',
possibleValues => <<'VALUES',
{
lineage => WebGUI::International::get('rank', 'Asset'),
title => WebGUI::International::get(99, 'Asset'),
className => WebGUI::International::get('type', 'Asset'),
revisionDate => WebGUI::International::get('revision date', 'Asset'),
assetSize => WebGUI::International::get('size', 'Asset'),
lockedBy => WebGUI::International::get('locked', 'Asset'),
}
VALUES
}, 4);
print "Done!\n" unless $quiet;
}
unless (WebGUI::ProfileField->new($session, $on)) {
print 'Adding Asset Manager Sort Direction profile field...'
unless $quiet;
WebGUI::ProfileField->create($session, $on => {
label =>
"WebGUI::International::get('$on label', 'Account_Profile')",
protected => 1,
fieldType => 'selectBox',
dataDefault => 'asc',
possibleValues => <<'VALUES',
{
asc => WebGUI::International::get('ascending', 'Account_Profile'),
desc => WebGUI::International::get('descending', 'Account_Profile'),
}
VALUES
}, 4);
print "Done!\n" unless $quiet;
}
}
#----------------------------------------------------------------------------
# Describe what our function does
#sub exampleFunction {
# my $session = shift;
# print "\tWe're doing some stuff here that you should know about... " unless $quiet;
# # and here's our code
# print "DONE!\n" unless $quiet;
#}
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
#----------------------------------------------------------------------------
# Add a package to the import node
sub addPackage {
my $session = shift;
my $file = shift;
print "\tUpgrading package $file\n" unless $quiet;
# Make a storage location for the package
my $storage = WebGUI::Storage->createTemp( $session );
$storage->addFileFromFilesystem( $file );
# Import the package into the import node
my $package = eval {
my $node = WebGUI::Asset->getImportNode($session);
$node->importPackage( $storage, {
overwriteLatest => 1,
clearPackageFlag => 1,
setDefaultTemplate => 1,
} );
};
if ($package eq 'corrupt') {
die "Corrupt package found in $file. Stopping upgrade.\n";
}
if ($@ || !defined $package) {
die "Error during package import on $file: $@\nStopping upgrade\n.";
}
return;
}
#-------------------------------------------------
sub start {
my $configFile;
$|=1; #disable output buffering
GetOptions(
'configFile=s'=>\$configFile,
'quiet'=>\$quiet
);
my $session = WebGUI::Session->open($webguiRoot,$configFile);
$session->user({userId=>3});
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->set({name=>"Upgrade to ".$toVersion});
return $session;
}
#-------------------------------------------------
sub finish {
my $session = shift;
updateTemplates($session);
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->commit;
$session->db->write("insert into webguiVersion values (".$session->db->quote($toVersion).",'upgrade',".time().")");
$session->close();
}
#-------------------------------------------------
sub updateTemplates {
my $session = shift;
return undef unless (-d "packages-".$toVersion);
print "\tUpdating packages.\n" unless ($quiet);
opendir(DIR,"packages-".$toVersion);
my @files = readdir(DIR);
closedir(DIR);
my $newFolder = undef;
foreach my $file (@files) {
next unless ($file =~ /\.wgpkg$/);
# Fix the filename to include a path
$file = "packages-" . $toVersion . "/" . $file;
addPackage( $session, $file );
}
}
#vim:ft=perl

View file

@ -190,6 +190,12 @@ sub callMethod {
);
return undef;
}
unless ($self->canView) {
my $session = $self->session;
$session->output->print($session->privilege->insufficient);
return undef;
}
#Try to call the method
my $output = eval { $self->$method(@{$args}) };

View file

@ -62,7 +62,7 @@ with 'WebGUI::Role::Asset::SetStoragePermissions';
use WebGUI::Storage;
use WebGUI::SQL;
use WebGUI::Event;
=head1 NAME
@ -219,6 +219,7 @@ sub exportWriteFile {
WebGUI::Error->throw(error => "can't copy " . $self->getStorageLocation->getPath($self->filename)
. ' to ' . $dest->absolute->stringify . ": $!");
}
fire $self->session, 'asset::export' => $dest;
}
#-------------------------------------------------------------------

View file

@ -254,14 +254,18 @@ Extends the base method to handle creating a new subscription group.
=cut
sub duplicate {
my $self = shift;
my $session = $self->session;
my $copy = $self->SUPER::duplicate(@_);
if ($self->get('subscriptionGroupId')) {
my $group = WebGUI::Group->new($session, $self->get('subscriptionGroupId'));
my $copied_group = WebGUI::Group->new($session, 'new');
$copied_group->addUsers($group->getUsers('withoutExpired'));
$copy->update({subscriptionGroupId => $copied_group->getId});
my $self = shift;
my $session = $self->session;
my $copy = $self->SUPER::duplicate(@_);
my $oldGroupId = $self->get('subscriptionGroupId');
if ($oldGroupId) {
my $newGroup = WebGUI::Group->new($session, 'new');
my $oldGroup = WebGUI::Group->new($session, $oldGroupId);
if ($oldGroup) {
$newGroup->addUsers($oldGroup->getUsers('withoutExpired'));
}
$copy->update({subscriptionGroupId => $newGroup->getId});
}
return $copy;
}

View file

@ -73,8 +73,6 @@ with 'WebGUI::Role::Asset::AlwaysHidden';
use WebGUI::International;
use JSON qw/from_json to_json/;
use Storable qw/dclone/;
use Data::Dumper;
=head1 NAME
@ -513,12 +511,10 @@ Returns the photo hash formatted as perl data. See also L<setPhotoData>.
sub getPhotoData {
my $self = shift;
if (!exists $self->{_photoData}) {
my $json = $self->photo;
$json ||= '[]';
$self->{_photoData} = from_json($json);
}
return dclone($self->{_photoData});
my $json = $self->photo;
$json ||= '[]';
my $photoData = from_json($json);
return $photoData;
}
#-------------------------------------------------------------------
@ -600,8 +596,6 @@ sub processEditForm {
my $session = $self->session;
$self->next::method;
my $archive = delete $self->{_parent}; ##Force a new lookup.
#$session->log->warn($self->getParent->get('className'));
#$session->log->warn($self->getParent->getParent->get('className'));
my $form = $session->form;
##Handle old data first, to avoid iterating across a newly added photo.
my $photoData = $self->getPhotoData;
@ -773,7 +767,6 @@ sub setPhotoData {
my $photo = to_json($photoData);
##Update the db.
$self->update({photo => $photo});
delete $self->{_photoData};
return;
}

View file

@ -705,7 +705,8 @@ sub getFeed {
=head2 getFeeds ( )
Gets an arrayref of hashrefs of all the feeds attached to this calendar.
Gets an arrayref of hashrefs of all the feeds attached to this calendar. Since the icalFeeds
property does double duty as JSON and Perl, deserialize from JSON if it's not already perl.
TODO: Format lastUpdated into the user's time zone
@ -713,7 +714,10 @@ TODO: Format lastUpdated into the user's time zone
sub getFeeds {
my $self = shift;
return $self->icalFeeds;
my $feeds = $self->icalFeeds;
return $feeds if (ref $feeds);
$self->session->log->warn('improperly stored icalFeed in calendar assetId:'.$self->getId);
return JSON::from_json($feeds);
}
#----------------------------------------------------------------------------

View file

@ -2075,7 +2075,7 @@ sub www_editThing {
maxEntriesPerUser=>undef,
maxEntriesTotal=>undef,
);
$thingId = $self->addThing(\%properties,0);
$thingId = "new";
}
else{
%properties = %{$self->getThing($thingId)};
@ -2600,13 +2600,14 @@ sub www_editFieldSave {
$defaultValue = $session->form->process("defaultFieldInThing");
}
$thingId = $self->addThing({ thingId => 'new' },0) if $thingId eq 'new';
$fieldId = $session->form->process("fieldId");
%properties = (
fieldId => $fieldId,
thingId => $thingId,
label => $label,
fieldType => $fieldType,
isUnique => $uniqueField,
isUnique => $uniqueField,
defaultValue => $defaultValue,
possibleValues => $session->form->process("possibleValues"),
pretext => $session->form->process("pretext"),
@ -2664,7 +2665,7 @@ sub www_editFieldSave {
# Make sure we send debug information along with the field.
$log->preventDebugOutput;
$session->output->print($newFieldId.$listItemHTML);
$session->output->print($thingId.$newFieldId.$listItemHTML);
return "chunked";
}

View file

@ -25,6 +25,7 @@ use URI::URL ();
use Scope::Guard qw(guard);
use WebGUI::ProgressTree;
use WebGUI::FormBuilder;
use WebGUI::Event;
=head1 NAME
@ -957,6 +958,7 @@ sub exportWriteFile {
$self->session->output->print($contents);
}
$fh->close;
fire $self->session, 'asset::export' => $dest;
}
#-------------------------------------------------------------------

148
lib/WebGUI/Event.pm Normal file
View file

@ -0,0 +1,148 @@
package WebGUI::Event;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2009 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use warnings;
use Exporter qw(import);
use WebGUI::Pluggable;
use Try::Tiny;
our @EXPORT = qw(fire);
=head1 NAME
WebGUI::Event
=head1 DESCRIPTION
Run custom code when things happen in WebGUI.
=head1 SUBSCRIBERS
If you're trying to handle an event, this is you.
=head2 CONFIG FILE
The C<events> hash in the config file maps names to lists of event handlers.
They will be run in the order they are defined. Instead of a list, you can
just specify one handler, and it will be treated as a list of one element.
The handlers are subroutines and must be able to be found by
WebGUI::Pluggable::run.
#...
"events" : {
"asset::export" : "My::Events::onExport",
"storage::addFile" : "My::Events::onFile"
},
#...
=head2 PERL CODE
Your code will be called with the arguments that are passed to
WebGUI::Event::Fire by the publisher.
package My::Events;
sub onExport {
my ($session, $name, $asset, $path) = @_;
#...
}
sub onFile {
my ($session, $name, $storage, $filename) = @_;
#...
}
=head1 PUBLISHERS
If you want to let people hook some behavior in the code you're writing, this
is you.
package WebGUI::Something;
use WebGUI::Event;
sub someThing {
#...
fire $session, 'something::happened', $with, $some, $arguments;
#...
}
=head1 SUBROUTINES
These subroutines are available from this package:
=cut
#-------------------------------------------------------------------
=head2 fire($session, $name, ...)
Exported by default. Calls all the subroutines defined in C<$session>'s config
file for C<$name> in order with these same arguments.
=cut
our %cache;
sub fire {
my ($session, $name) = splice @_, 0, 2;
my $config = $session->config;
my $path = $config->getFilePath;
unless (exists $cache{$path}{$name}) {
my $events = $config->get('events') or return;
my $names = $events->{$name} or return;
$names = [ $names ] unless ref $names eq 'ARRAY';
$cache{$path}{$name} = [
grep { $_ } map {
if ($_) {
my ($package, $subname) = /^(.*)::([^:]+)$/;
try {
WebGUI::Pluggable::load($package);
$package->can($subname);
}
catch {
$session->log->error(
"Couldn't load event handler for $name: $_"
);
undef;
};
}
} @$names
];
}
$_->($session, $name, @_) for @{ $cache{$path}{$name} };
}
=head1 RATIONALE
=head2 Why can't I register listeners at runtime? or...
=head2 Why is there no subscribe method? or...
=head2 Why is this in the config file instead of somewhere else?
WebGUI::Events are conceptually per-site things. The code to be called is
static and hopefully controlled someone by with access to the config file.
That being said, you could certainly build something more dynamic on top of
this system. Writing an event handler that publishes messages to a broker
service like DBus or RabbitMQ is entirely possible.
=cut
1;

View file

@ -1634,6 +1634,7 @@ sub resetGroupFields {
##Note, I did assets in SQL instead of using the API because you would have to
##instanciate every version of the asset that used the group. This should be much quicker
ASSET: foreach my $assetClass ($db->buildArray('SELECT DISTINCT className FROM asset')) {
next ASSET unless $db->quickScalar( "SELECT COUNT(*) FROM asset WHERE className=?", [$assetClass] );
my $className = eval { WebGUI::Asset->loadModule($assetClass); };
if (my $e = Exception::Class->caught) {
warn $e->cause;
@ -1685,7 +1686,11 @@ sub resetGroupFields {
push @activities, @{ $wfActivities };
}
foreach my $activity (@activities) {
my $definition = WebGUI::Pluggable::instanciate($activity, 'definition', [$session]);
my $definition = eval { WebGUI::Pluggable::instanciate($activity, 'definition', [$session]) };
if ( $@ ) {
$session->log->warn( "Couldn't instanciate activity class $activity to reset groups, skipping..." );
next;
}
my $sth = $db->prepare('UPDATE WorkflowActivityData set value=3 where name=? and value=?');
SUBDEF: foreach my $subdef (@{ $definition }) {
PROP: while (my ($fieldName, $properties) = each %{ $subdef->{properties} }) {

View file

@ -45,7 +45,7 @@ sub process {
my ($session, $identifier, $type) = @_;
if (!$identifier) {
$session->errorHandler->warn('AssetProxy macro called without an asset to proxy. '
. 'The macro was called through this url: '.$session->asset->get('url'));
. 'The macro was called through this url: '.$session->url->page);
if ($session->isAdminOn) {
my $i18n = WebGUI::International->new($session, 'Macro_AssetProxy');
return $i18n->get('invalid url');
@ -61,9 +61,9 @@ sub process {
else {
$asset = eval { WebGUI::Asset->newByUrl($session,$identifier); };
}
if (Exception::Class->caught()) {
$session->log->warn('AssetProxy macro called invalid asset: '.$identifier
.'. The macro was called through this url: '.$session->asset->get('url'));
if (!defined $asset) {
$session->errorHandler->warn('AssetProxy macro called invalid asset: '.$identifier
.'. The macro was called through this url: '.$session->url->page);
if ($session->isAdminOn) {
my $i18n = WebGUI::International->new($session, 'Macro_AssetProxy');
return $i18n->get('invalid url');
@ -71,15 +71,15 @@ sub process {
}
elsif ($asset->get('state') =~ /^trash/) {
$session->log->warn('AssetProxy macro called on asset in trash: '.$identifier
.'. The macro was called through this url: '.$session->asset->get('url'));
.'. The macro was called through this url: '.$session->url->page);
if ($session->isAdminOn) {
my $i18n = WebGUI::International->new($session, 'Macro_AssetProxy');
return $i18n->get('asset in trash');
}
}
elsif ($asset->get('state') =~ /^clipboard/) {
$session->log->warn('AssetProxy macro called on asset in clipboard: '.$identifier
.'. The macro was called through this url: '.$session->asset->get('url'));
$session->errorHandler->warn('AssetProxy macro called on asset in clipboard: '.$identifier
.'. The macro was called through this url: '.$session->url->page);
if ($session->isAdminOn) {
my $i18n = WebGUI::International->new($session, 'Macro_AssetProxy');
return $i18n->get('asset in clipboard');

View file

@ -41,23 +41,26 @@ sub process {
my ($session, $thingDataUrl, $templateHint ) = @_;
my $i18n = WebGUI::International->new($session, 'Macro_RenderThingData');
return $i18n->get('no template') if !$templateHint;
my $uri = URI->new( $thingDataUrl );
my $gateway = $session->config->get('gateway');
my $uri = URI->new( $thingDataUrl );
my $thingy_url = $uri->path;
$thingy_url =~ s/^$gateway//;
my $urlHash = { $uri->query_form };
my $thingId = $urlHash->{'thingId'};
my $thingDataId = $urlHash->{'thingDataId'};
my $thing = WebGUI::Asset::Wobject::Thingy->newByUrl( $session, $uri->path );
my $thing = WebGUI::Asset::Wobject::Thingy->newByUrl( $session, $thingy_url );
# TODO: i18n
return ( "Bad URL: " . $thingDataUrl ) if !$thing || !$thingId || !$thingDataId;
return ( $i18n->get('bad url') . $thingDataUrl ) if !$thing || !$thingId || !$thingDataId;
# Render
my $output = $thing->www_viewThingData( $thingId, $thingDataId, $templateHint );
# FIX: Temporary solution (broken map due to template rendering <script> tags)
return "RenderThingData: Contained bad tags!" if $output =~ /script>/;
return $i18n->get('bad tags') if $output =~ /script>/;
return $output;
}

View file

@ -513,7 +513,9 @@ A reference to the current session.
sub www_leaveVersionTag {
my $session = shift;
WebGUI::VersionTag->getWorking($session)->leaveTag;
my $tag = $session->scratch()->get(q{versionTag});
WebGUI::VersionTag->getWorking($session)->leaveTag if $tag;
return www_manageVersions($session);
}

View file

@ -286,12 +286,14 @@ sub www_sendToPayPal {
my $url = $session->url;
my $base = $url->getSiteURL . $url->page;
my $i18n = WebGUI::International->new( $self->session, $I18N );
my $returnUrl = URI->new($base);
$returnUrl->query_form( {
shop => 'pay',
method => 'do',
do => 'payPalCallback',
paymentGatewayId => $self->getId,
LOCALECODE => $i18n->getLanguage->{locale},
}
);
@ -310,7 +312,6 @@ sub www_sendToPayPal {
my $testMode = $self->testMode;
my $response = LWP::UserAgent->new->post( $self->apiUrl, $form );
my $params = $self->responseHash($response);
my $i18n = WebGUI::International->new( $self->session, $I18N );
my $error;
if ($params) {

View file

@ -225,6 +225,7 @@ sub paymentVariables {
my $url = $self->session->url;
my $base = $url->getSiteURL . $url->page;
my $cart = $self->getCart;
my $i18n = WebGUI::International->new($self->session);
my $return = URI->new($base);
$return->query_form( {
@ -247,10 +248,11 @@ sub paymentVariables {
return => $return->as_string,
cancel_return => $cancel->as_string,
lc => $i18n->getLanguage->{locale},
handling_cart => $cart->calculateShipping, ##According to https://www.x.com/message/180018#180018
#handling_cart => $cart->calculateShipping, ##According to https://www.x.com/message/180018#180018
tax_cart => $cart->calculateTaxes,
discount_amount_cart => -($cart->calculateShopCreditDeduction),
discount_amount_cart => abs($cart->calculateShopCreditDeduction),
# When we verify that we have a valid transaction ID later on in
# processPayment, we'll make sure it's the cart we think it is.
@ -259,11 +261,18 @@ sub paymentVariables {
my $counter = 0;
foreach my $item (@{ $cart->getItems}) {
my $n = ++$counter;
$params{"amount_$n"} = $item->getSku->getPrice;
$params{"quantity_$n"} = $item->quantity;
$params{"item_name_$n"} = $item->configuredTitle;
$params{"item_number_$n"} = $item->itemId;
++$counter;
$params{"amount_$counter"} = $item->getSku->getPrice;
$params{"quantity_$counter"} = $item->get('quantity');
$params{"item_name_$counter"} = $item->get('configuredTitle');
$params{"item_number_$counter"} = $item->get('itemId');
}
if ($cart->requiresShipping) {
++$counter;
$params{"amount_$counter"} = $cart->calculateShipping;
$params{"quantity_$counter"} = 1;
$params{"item_name_$counter"} = $i18n->get('shipping', 'Shop');
$params{"item_number_$counter"} = 'Shipping';
}
return \%params;

View file

@ -26,6 +26,7 @@ use Image::Magick;
use Path::Class::Dir;
use Storable ();
use WebGUI::Paths;
use WebGUI::Event;
use JSON ();
@ -111,6 +112,19 @@ sub _addError {
#-------------------------------------------------------------------
=head2 _addFile ( $filename )
Emits the storage::addFile event for this storage/filename.
=cut
sub _addFile {
my ($self, $filename) = @_;
fire $self->session, 'storage::addFile', $self, $filename;
}
#-------------------------------------------------------------------
=head2 _cdnAdd ( )
Adds to CDN queue, for any of the add* methods.
@ -338,6 +352,7 @@ sub addFileFromFilesystem {
close $dest;
close $source;
$self->_cdnAdd;
$self->_addFile($filename);
return $filename;
}
@ -390,6 +405,7 @@ sub addFileFromFormPost {
$attachmentCount++;
if (File::Copy::move($upload->path, $filePath)) {
$self->_changeOwner($filePath);
$self->_addFile($filename);
$self->session->log->info("Got ".$upload->filename);
}
else {
@ -426,6 +442,7 @@ sub addFileFromHashref {
Storable::nstore($hashref, $self->getPath($filename))
or $self->_addError("Couldn't create file ".$self->getPath($filename)." because ".$!);
$self->_changeOwner($self->getPath($filename));
$self->_addFile($filename);
$filename and $self->_cdnAdd;
return $filename;
}
@ -455,6 +472,7 @@ sub addFileFromScalar {
print $FILE $content;
close($FILE);
$self->_changeOwner($self->getPath($filename));
$self->_addFile($filename);
$self->_cdnAdd;
}
else {
@ -586,7 +604,12 @@ sub copy {
else {
open my $source, '<:raw', $origFile or next FILE;
open my $dest, '>:raw', $copyFile or next FILE;
File::Copy::copy($source, $dest) or $self->_addError("Couldn't copy file ".$origFile." to ".$copyFile." because ".$!);
if (File::Copy::copy($source, $dest)) {
$newStorage->_addFile($file);
}
else {
$self->_addError("Couldn't copy file $origFile to $copyFile because $!");
}
close $dest;
close $source;
}
@ -619,6 +642,7 @@ sub copyFile {
File::Copy::copy( $self->getPath($filename), $self->getPath($newFilename) )
|| croak "Couldn't copy '$filename' to '$newFilename': $!";
$self->_changeOwner($self->getPath($filename));
$self->_addFile($filename);
$self->_cdnAdd;
return undef;

View file

@ -843,7 +843,7 @@ Example call:
},
'WebGUI::LDAPLink' => sub {
my $link = shift;
$link->session->db->write("delete from ldapLink where ldapLinkId=?", [$link->{ldapLinkId}]);
$link->session->db->write("delete from ldapLink where ldapLinkId=?", [$link->{_ldapLinkId}]);
},
'CODE' => sub {
(shift)->();

View file

@ -169,6 +169,24 @@ our $I18N = {
message => q{required },
lastUpdated => 1225724810,
},
'assetManagerSortDirection label' => {
message => 'Asset Manager Sort Direction',
lastUpdated => 1307982524,
},
'assetManagerSortColumn label' => {
message => 'Asset Manager Sort Column',
lastUpdated => 1307982524,
},
'ascending' => {
message => 'Ascending',
lastUpdated => 1307982524,
context => 'Ascending sort order (lowest to highest)',
},
'descending' => {
message => 'Descending',
lastUpdated => 1307982524,
context => 'Descending sort order (highest to lowest)',
},
};
1;

View file

@ -1809,6 +1809,12 @@ the Collaboration Asset, the user will be notified.|,
lastUpdated => 0,
},
'View this message on the website' => {
message => q{View this message on the website},
context => q{label in the default notification email template},
lastUpdated => 0,
},
};
1;

View file

@ -1150,6 +1150,18 @@ below/after the form element.|,
context => q|Template variable help for getViewThingData|,
},
'unique label' => {
message => q|Unique|,
lastUpdated => 1308006166,
context => q|Label in the edit field screen.|,
},
'unique description' => {
message => q|Is this a unique field? In simple terms, values in unique fields can't have any duplicates in other rows.|,
lastUpdated => 1308006162,
context => q|Hoverhelp for edit field screen|,
},
};
1;

View file

@ -4,13 +4,19 @@ use strict;
our $I18N = {
'bad tags' => {
message => q||,
message => q|RenderThingData: Thingy output contains script tags.|,
lastUpdated => 1306275259,
},
'no template' => {
message => q|RenderThingData: Please specify a template.|,
lastUpdated => 1149177662,
lastUpdated => 1306337467,
},
'bad url' => {
message => q|Bad URL: |,
lastUpdated => 1306337468,
context => q|A URL with missing parameters or one that does not refer to a Thingy asset.|,
},
};

File diff suppressed because one or more lines are too long

View file

@ -882,6 +882,7 @@ sub getTitleTests {
subtest 'canAdd tolerates being called as an object method', sub {
my $class = 'WebGUI::Asset::Snippet';
my $snip = $tempNode->addChild({className => $class});
WebGUI::Test->addToCleanup($snip);
# Make a test user who's just in Turn Admin On
my $u = WebGUI::User->create($session);

View file

@ -20,6 +20,7 @@ use WebGUI::Test; # Must use this before any other WebGUI modules
use WebGUI::Session;
use WebGUI::Asset;
use WebGUI::Exception;
use WebGUI::Test::Event;
use Cwd;
use Exception::Class;
@ -36,7 +37,27 @@ my $session = WebGUI::Test->session;
#----------------------------------------------------------------------------
# Tests
plan tests => 128; # Increment this number for each test you create
WebGUI::Test->originalConfig('exportPath');
my @events;
my $testRan = 1;
plan tests => 125; # Increment this number for each test you create
sub export_ok {
my ($asset, $message) = @_;
subtest $message => sub {
plan tests => 3;
my $e;
my @events = trap {
eval { $asset->exportWriteFile() };
$e = $@;
} $asset->session, 'asset::export';
ok !$e, 'ran without exception';
is scalar @events, 1, 'event fired once';
is $events[0][2], $asset->exportGetUrlAsPath->absolute;
};
}
#----------------------------------------------------------------------------
# exportCheckPath()
@ -385,11 +406,7 @@ my $content;
my $guid = $session->id->generate;
my $guidPath = Path::Class::Dir->new($config->get('uploadsPath'), 'temp', $guid);
$config->set('exportPath', $guidPath->absolute->stringify);
eval { $parent->exportWriteFile() };
is($@, '', "exportWriteFile works when creating exportPath");
# ensure that the file was actually written
ok(-e $parent->exportGetUrlAsPath->absolute->stringify, "exportWriteFile actually writes the file when creating exportPath");
export_ok $parent, 'exportWriteFile works when creating exportPath';
# now make sure that it contains the correct content
$content = WebGUI::Test->getPage2(
@ -444,7 +461,7 @@ eval { $firstChild->exportWriteFile() };
is($@, '', "exportWriteFile works for first_child");
# ensure that the file was actually written
ok(-e $firstChild->exportGetUrlAsPath->absolute->stringify, "exportWriteFile actually writes the first_child file");
export_ok $firstChild, 'exportWriteFile works for first_child';
# verify it has the correct contents
eval { $content = WebGUI::Test->getPage2( $firstChild->get('url').'?func=exportHtml_view', ) };
@ -456,11 +473,7 @@ $guidPath->rmtree;
$session->response->setNoHeader(1);
$session->user( { userId => 1 } );
eval { $grandChild->exportWriteFile() };
is($@, '', "exportWriteFile works for grandchild");
# ensure that the file was written
ok(-e $grandChild->exportGetUrlAsPath->absolute->stringify, "exportWriteFile actually writes the grandchild file");
export_ok $grandChild, 'exportWriteFile works for grandchild';
# finally, check its contents
$session->style->sent(0);
@ -471,17 +484,12 @@ is(scalar $grandChild->exportGetUrlAsPath->absolute->slurp, $content, "exportWri
$guidPath->rmtree;
$asset = WebGUI::Asset->newById($session, 'ExportTest000000000001');
$session->response->setNoHeader(1);
eval { $asset->exportWriteFile() };
is($@, '', 'exportWriteFile for perl file works');
ok(-e $asset->exportGetUrlAsPath->absolute->stringify, "exportWriteFile actually writes the perl file");
export_ok $asset, 'exportWriteFile for perl file works';
$guidPath->rmtree;
$asset = WebGUI::Asset->newById($session, 'ExportTest000000000002');
eval { $asset->exportWriteFile() };
is($@, '', 'exportWriteFile for plain file works');
ok(-e $asset->exportGetUrlAsPath->absolute->stringify, "exportWriteFile actuall writes the plain file");
$asset = WebGUI::Asset->new($session, 'ExportTest000000000002');
export_ok $asset, 'exportWriteFile for plain file works';
$guidPath->rmtree;
@ -492,8 +500,11 @@ $guidPath->rmtree;
# permissions on something.
$parent->update( { groupIdView => 3 } ); # admins
$session->response->setNoHeader(1);
eval { $parent->exportWriteFile() };
$e = Exception::Class->caught();
@events = trap {
eval { $parent->exportWriteFile() };
$e = Exception::Class->caught();
} $session, 'asset::export';
isa_ok($e, 'WebGUI::Error', "exportWriteFile throws when user can't view asset");
cmp_deeply(
$e,
@ -507,6 +518,7 @@ cmp_deeply(
# no directory or file written
ok(!-e $parent->exportGetUrlAsPath->absolute->stringify, "exportWriteFile doesn't write file when user can't view asset");
ok(!-e $parent->exportGetUrlAsPath->absolute->parent, "exportWriteFile doesn't write directory when user can't view asset");
is scalar @events, 0, 'event not fired';
# undo our viewing changes
$parent->update( { groupIdView => 7 } ); # everyone

View file

@ -270,6 +270,7 @@ subtest 'asset metadata versioning' => sub {
is $meta->get(), 'version one', 'v1 has not been changed';
my $dup = $asset->duplicate;
WebGUI::Test->addToCleanup($dup);
my $db = $session->db;
my $count_rev = sub {

View file

@ -41,10 +41,10 @@ my $map = $node->addChild({
# Create a map point
my $test_point = {
website => 'http://www.plainblack.com',
address1 => '520 University Ave',
address1 => '520 University',
address2 => 'Suite 320',
city => 'Madison',
region => 'WI',
region => 'Wisconsin',
zipCode => '53703',
country => 'United States',
phone => '608-555-1212',

View file

@ -0,0 +1,50 @@
# vim:syntax=perl
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#------------------------------------------------------------------
=head1 BUG DESCRIPTION
Thread's duplicate method fails if the subscriptionGroupId isn't a valid group
(for instance, if it was imported from another site). It should just not copy
the group in that case.
=cut
use warnings;
use strict;
use Test::More tests => 4;
use Test::Exception;
use FindBin;
use lib "$FindBin::Bin/../../../lib";
use WebGUI::Test;
use WebGUI::Asset;
my $session = WebGUI::Test->session;
my $collab = WebGUI::Test->asset( className => 'WebGUI::Asset::Wobject::Collaboration' );
my $thread = $collab->addChild(
{
className => 'WebGUI::Asset::Post::Thread',
subscriptionGroupId => $session->id->generate(),
}
);
WebGUI::Test->addToCleanup($thread);
SKIP: {
my $copy;
skip('duplicate died', 3) unless
lives_ok { $copy = $thread->duplicate() } q"duplicate() doesn't die";
WebGUI::Test->addToCleanup($copy);
my $groupId = $copy->get('subscriptionGroupId');
ok $groupId, 'Copy has a group id';
isnt $groupId, $thread->get('subscriptionGroupId'), '...a different one';
ok(WebGUI::Group->new($session, $groupId), '...and it instantiates');
};

View file

@ -26,6 +26,10 @@ my %tag = ( tagId => $versionTag->getId, status => "pending" );
addToCleanup($versionTag);
my $snippet = $node->addChild({className=>'WebGUI::Asset::Snippet', %tag});
# Make sure TemplateToolkit is in the config file
WebGUI::Test->originalConfig( 'templateParsers' );
$session->config->addToArray( 'templateParsers' => 'WebGUI::Asset::Template::TemplateToolkit' );
# Test for a sane object type
isa_ok($snippet, 'WebGUI::Asset::Snippet');

View file

@ -8,6 +8,9 @@
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use Test::MockTime qw/:all/;
use FindBin;
use strict;
use WebGUI::Test;
@ -77,8 +80,9 @@ WebGUI::Test->addToCleanup($storage1, $storage2);
#
############################################################
my $tests = 45;
plan tests => 1 + $tests
my $tests = 48;
plan tests => 1
+ $tests
+ $canEditMaker->plan
;
@ -459,4 +463,61 @@ cmp_bag(
) or diag Dumper( $keyword_loop );
$session->scratch->delete('isExporting');
############################################################
#
# addRevision, copying and duplicating photo data
#
############################################################
set_relative_time(-70);
my $rev_story = $archive->addChild({
className => 'WebGUI::Asset::Story',
title => 'Story revision',
subtitle => 'The story of a CMS',
byline => 'C.F. Kuskie',
story => 'Revisioning a story should not cause the photo information to be lost.',
}, undef, undef, { skipAutoCommitWorkflows => 1, skipNotification => 1, });
my $tag = WebGUI::VersionTag->getWorking($session);
$tag->commit;
my $rev_story = $rev_story->cloneFromDb;
my $rev_storage = WebGUI::Storage->create($session);
$rev_story->setPhotoData([{
byLine => 'C Forest Kuskie',
caption => 'ugly old hacker',
storageId => $rev_storage->getId,
}]);
cmp_deeply(
$rev_story->getPhotoData,
[{
byLine => 'C Forest Kuskie',
caption => 'ugly old hacker',
storageId => $rev_storage->getId,
}],
'setup for add revision test, photo data'
);
restore_time();
my $revision = $rev_story->addRevision({}, undef, undef, { skipAutoCommitWorkflows => 1, skipNotification => 1, });
cmp_deeply(
$revision->getPhotoData,
[{
byLine => 'C Forest Kuskie',
caption => 'ugly old hacker',
storageId => ignore(),
}],
'revision has a copy of most of the photo data'
);
my $revision_storageId = $revision->getPhotoData->[0]->{storageId};
ok($revision_storageId && ($revision_storageId ne $rev_storage->getId), 'storageId in the revision is different from the original');
#vim:ft=perl

View file

@ -55,7 +55,7 @@ use Data::Dumper;
use WebGUI::Asset::Wobject::Calendar;
use WebGUI::Asset::Event;
plan tests => 12 + scalar @icalWrapTests;
plan tests => 14 + scalar @icalWrapTests;
my $session = WebGUI::Test->session;
@ -314,8 +314,6 @@ addToCleanup($tag2);
is(scalar @{ $windowCal->getLineage(['children'])}, 17, 'added events to the window calendar');
diag "startDate: ". $windowStart->toDatabase;
diag "endDate: ". $windowEnd->toDatabase;
my @window = $windowCal->getEventsIn($windowStart->toDatabase, $windowEnd->toDatabase);
cmp_bag(
@ -590,3 +588,13 @@ cmp_deeply(
[],
'getFeeds: returns an empty array ref with no feeds'
);
##Update with JSON and try again :)
$feedCal->update({icalFeeds => '[]'});
is_deeply $feedCal->get('icalFeeds'), [], 'set as JSON, returned perl';
cmp_deeply(
$feedCal->getFeeds(),
[],
'but getFeeds still returns a data structure.'
);

View file

@ -101,7 +101,7 @@ cmp_deeply(
title => 'Dummy Title',
description => 'Dummy Synopsis', ##Not description
link => $session->url->getSiteURL . '/home/shawshank',
copyright => undef,
copyright => bool(0),
),
'... title, description, link inherit from asset by default, copyright unset'
);

70
t/Event.t Normal file
View file

@ -0,0 +1,70 @@
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#------------------------------------------------------------------
use strict;
use warnings;
use FindBin;
use Test::More tests => 12;
use lib "$FindBin::Bin/lib";
use WebGUI::Test;
use WebGUI::Session;
use WebGUI::Event;
my $session = WebGUI::Test->session;
WebGUI::Test->originalConfig('events');
my $config = $session->config;
$config->set('events/foo', [
'My::Events::onFoo',
'My::Events::onFoo2'
]);
$config->set('events/bar', 'My::Events::onBar');
my ($foo, $foo2, $bar) = @_;
sub My::Events::onFoo {
my ($session, $name, $one, $two, $three) = @_;
isa_ok $session, 'WebGUI::Session', 'onFoo: session';
is $name, 'foo', "onFoo: $name";
$foo = $one;
}
sub My::Events::onFoo2 {
my ($session, $name, $one, $two, $three) = @_;
isa_ok $session, 'WebGUI::Session', 'onFoo2: session';
is $name, 'foo', "onFoo2: $name";
$foo2 = $two;
}
sub My::Events::onBar {
my ($session, $name, $one, $two, $three) = @_;
isa_ok $session, 'WebGUI::Session', 'onBar: session';
is $name, 'bar', "onBar: $name";
$bar = $three;
}
# Tell require that My::Events is already loaded.
$INC{'My/Events.pm'} = __FILE__;
fire $session, 'foo', qw(first second third);
is $foo, 'first', 'foo called';
is $foo2, 'second', 'foo2 called';
ok !defined $bar, 'bar not called';
undef $foo;
undef $foo2;
fire $session, 'bar', qw(first second third);
ok !defined $foo, 'foo not called';
ok !defined $foo2, 'foo2 not called';
is $bar, 'third', 'onBar called';
#vim:ft=perl

View file

@ -32,7 +32,7 @@ my $session = WebGUI::Test->session;
$templateMock->mock_id( $templateId );
$templateMock->mock_url( $templateUrl );
plan tests => 4;
plan tests => 6;
my $node = WebGUI::Test->asset;
my $thingy = $node->addChild({
@ -93,5 +93,17 @@ $templateProcessed = 0;
$output = WebGUI::Macro::RenderThingData::process($session, $thing_url, $templateUrl);
ok $templateProcessed, 'passed template url, template processed';
$templateProcessed = 0;
WebGUI::Test->originalConfig('gateway');
$session->config->set('gateway', '/gated');
my $thing_url = $thingy->getUrl('thingId='.$thingId.';thingDataId='.$thingDataId);
$output = WebGUI::Macro::RenderThingData::process($session, $thing_url, $templateId);
ok $templateProcessed, 'gateway set, passed templateId, template processed';
$templateProcessed = 0;
$output = WebGUI::Macro::RenderThingData::process($session, $thing_url, $templateUrl);
ok $templateProcessed, '... passed template url, template processed';
$templateProcessed = 0;

View file

@ -36,8 +36,8 @@ $session->user({userId => 3});
my $addExceptions = getAddExceptions($session);
plan tests => 79
+ 2*scalar(@{$addExceptions});
my $tests = 80 + 2*scalar(@{$addExceptions});
plan tests => $tests;
WebGUI::Test->addToCleanup(SQL => 'delete from tax_generic_rates');
@ -526,6 +526,20 @@ cmp_deeply(
'getTaxRates: return correct data for a state when the address has alternations'
);
my $capitalized = $taxer->add({
country => 'USA',
state => 'wi',
taxRate => '50',
});
cmp_deeply(
$taxer->getTaxRates($taxingAddress),
[0, 5, 0.5],
'... multiple entries with different capitalization, first matches'
);
$taxer->delete({ taxId => $capitalized });
#######################################################################
#
# calculate

View file

@ -11,6 +11,7 @@
use strict;
use WebGUI::Test;
use WebGUI::Test::Event;
use WebGUI::Session;
use WebGUI::Storage;
@ -29,7 +30,7 @@ my $cwd = Cwd::cwd();
my ($extensionTests, $fileIconTests, $block_extension_tests) = setupDataDrivenTests($session);
plan tests => 155
plan tests => 161
+ scalar @{ $extensionTests }
+ scalar @{ $fileIconTests }
+ scalar @{ $block_extension_tests }
@ -196,7 +197,16 @@ Hi, I'm a file.
I have two lines.
EOCON
my $filename = $storage1->addFileFromScalar('content', $content);
sub fired_ok(&@) {
my ($block, @expected) = @_;
my @events = trap { $block->() } $session, 'storage::addFile';
my @got = map { $_->[3] } @events;
cmp_bag \@got, \@expected, 'events fired for ' . join ', ', @expected;
}
my $filename; fired_ok {
$filename = $storage1->addFileFromScalar('content', $content)
} 'content';
is ($filename, 'content', 'processed filename returned by addFileFromScalar');
@ -236,10 +246,10 @@ foreach my $extTest (@{ $extensionTests }) {
my $fileStore = WebGUI::Storage->create($session);
WebGUI::Test->addToCleanup($fileStore);
cmp_bag($fileStore->getFiles(1), ['.'], 'Starting with an empty storage object, no files in here except for . ');
$fileStore->addFileFromScalar('.dotfile', 'dot file');
fired_ok { $fileStore->addFileFromScalar('.dotfile', 'dot file') } '.dotfile';
cmp_bag($fileStore->getFiles(), [ ], 'getFiles() by default does not return dot files');
cmp_bag($fileStore->getFiles(1), ['.', '.dotfile'], 'getFiles(1) returns all files, including dot files');
$fileStore->addFileFromScalar('dot.file', 'dot.file');
fired_ok { $fileStore->addFileFromScalar('dot.file', 'dot.file') } 'dot.file';
cmp_bag($fileStore->getFiles(), ['dot.file'], 'getFiles() returns normal files');
cmp_bag($fileStore->getFiles(1), ['.', '.dotfile', 'dot.file'], 'getFiles(1) returns all files, including dot files');
@ -260,7 +270,9 @@ is($obj->stringify, $storage1->getPath, '... Path::Class::Dir object has correct
####################################################
my $storageHash = {'blah'=>"blah",'foo'=>"foo"};
$storage1->addFileFromHashref("testfile-hash.file", $storageHash);
fired_ok {
$storage1->addFileFromHashref("testfile-hash.file", $storageHash);
} 'testfile-hash.file';
ok (-e $storage1->getPath("testfile-hash.file"), 'addFileFromHashRef creates file');
####################################################
@ -298,9 +310,11 @@ ok (!(-e $storage1->getPath("testfile-hash.file")), "rename file original file i
#
####################################################
$storage1->addFileFromFilesystem(
WebGUI::Test->getTestCollateralPath('littleTextFile'),
);
fired_ok {
$storage1->addFileFromFilesystem(
WebGUI::Test->getTestCollateralPath('littleTextFile'),
);
} 'littleTextFile';
ok(
grep(/littleTextFile/, @{ $storage1->getFiles }),
@ -334,7 +348,9 @@ cmp_bag($s3copy->getFiles(), [ @filesToCopy ], 'copy: passing explicit variable
my $deepDeepDir = $deepDir->subdir('deep');
my $errorStr;
my @foo = $deepDeepDir->mkpath({ error => \$errorStr } );
$deepStorage->addFileFromScalar('deep/file', 'deep file');
fired_ok {
$deepStorage->addFileFromScalar('deep/file', 'deep file')
} 'deep/file';
cmp_bag(
$deepStorage->getFiles('all'),
[ '.', 'deep', 'deep/file' ],

View file

@ -19,7 +19,7 @@ use WebGUI::User;
use WebGUI::ProfileField;
use WebGUI::Shop::AddressBook;
use Test::More tests => 233; # increment this value for each test you create
use Test::More tests => 235; # increment this value for each test you create
use Test::Deep;
use Data::Dumper;
@ -74,6 +74,7 @@ cmp_ok(abs($user->lastUpdated-$lastUpdate), '<=', 1, 'lastUpdated() -- status ch
$user->status('Selfdestructed');
is($user->status, "Selfdestructed", 'status("Selfdestructed")');
is($user->get('status'), "Selfdestructed", 'status("Selfdestructed") via get');
# Deactivation user deletes all sessions and scratches
@ -83,6 +84,7 @@ $newSession->scratch->set("hasStapler" => "no");
$user->status('Deactivated');
is($user->status, "Deactivated", 'status("Deactivated")');
is($user->get('status'), "Deactivated", 'status("Deactivated") via get');
ok(
!$session->db->quickScalar("SELECT COUNT(*) from userSession where userId=?",[$user->userId]),

32
t/_bug.skeleton Normal file
View file

@ -0,0 +1,32 @@
# vim:syntax=perl
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#------------------------------------------------------------------
=head1 BUG DESCRIPTION
Blah blah blah, whatever the bug poster said in the initial post plus any
relevant clarification from the discussion thread.
=cut
use warnings;
use strict;
use Test::More tests => 0;
use FindBin;
use lib "$FindBin::Bin/../lib";
use WebGUI::Test;
my $session = WebGUI::Test->session;
# A bug test should test the bug it is named for and be placed in an
# appropriate place in the test tree. For example, if bug #34721 was a Snippet
# bug, it would go in t/Asset/Snippet/bug_34721_short_description.t.

View file

@ -0,0 +1,78 @@
package WebGUI::Test::Event;
use List::Util qw(first);
use Exporter qw(import);
our @EXPORT = qw(trap);
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2009 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use warnings;
=head1 SYNOPSIS
Temporarily handle WebGUI::Events.
=head1 METHODS
These methods are available from this class:
=cut
our $session;
our @names;
our @trap;
my $handlerName = __PACKAGE__ . '::handler';
sub handler {
my ($s, $n) = @_;
return unless first { $_ eq $n } @names;
push @trap, \@_;
};
#-------------------------------------------------------------------
=head2 trap ($code, $session, @names)
Traps the events named by @names and returns them as a list of arrayrefs in
the order they occured. The arrayrefs are all arguments passed to the event
handler.
=cut
sub trap(&$@) {
my $block = shift;
local ($session, @names) = @_;
local @trap;
my $config = $session->config;
my $events = $config->get('events');
local %WebGUI::Event::cache;
for my $name (@names) {
$config->set("events/$name", $handlerName);
}
eval { $block->() };
my $err = $@;
if ($events) {
$config->set(events => $events);
}
else {
$config->delete('events');
}
die $err if $err;
return @trap;
}

View file

@ -7,6 +7,7 @@
if ( typeof WebGUI == "undefined" ) {
WebGUI = {};
}
if ( typeof WebGUI.AssetManager == "undefined" ) {
WebGUI.AssetManager = {};
}
@ -82,37 +83,52 @@ WebGUI.AssetManager.findRow = function ( child ) {
}
};
WebGUI.AssetManager.assetActionCache = {};
/*---------------------------------------------------------------------------
WebGUI.AssetManager.formatActions ( )
Format the Edit and More links for the row
*/
WebGUI.AssetManager.formatActions = function ( elCell, oRecord, oColumn, orderNumber ) {
if ( oRecord.getData( 'actions' ) ) {
elCell.innerHTML
= '<a href="' + WebGUI.AssetManager.appendToUrl(oRecord.getData( 'url' ), 'func=edit;proceed=manageAssets') + '">'
+ WebGUI.AssetManager.i18n.get('Asset', 'edit') + '</a>'
+ ' | '
;
}
else {
elCell.innerHTML = "";
}
var more = document.createElement( 'a' );
elCell.appendChild( more );
more.appendChild( document.createTextNode( WebGUI.AssetManager.i18n.get('Asset','More' ) ) );
more.href = '#';
var data = oRecord.getData(),
id = data.assetId,
assets = WebGUI.AssetManager.assetActionCache,
asset = assets[id],
edit, more;
// Delete the old menu
if ( document.getElementById( 'moreMenu' + oRecord.getData( 'assetId' ) ) ) {
var oldMenu = document.getElementById( 'moreMenu' + oRecord.getData( 'assetId' ) );
oldMenu.parentNode.removeChild( oldMenu );
elCell.innerHTML = '';
if (!data.actions) {
return;
}
var options = WebGUI.AssetManager.buildMoreMenu(oRecord.getData( 'url' ), more, oRecord.getData( 'actions' ));
if (!asset) {
assets[id] = asset = {};
asset.data = data;
asset.bar = document.createTextNode(' | ');
var menu = new YAHOO.widget.Menu( "moreMenu" + oRecord.getData( 'assetId' ), options );
YAHOO.util.Event.onDOMReady( function () { menu.render( document.getElementById( 'assetManager' ) ); } );
YAHOO.util.Event.addListener( more, "click", function (e) { YAHOO.util.Event.stopEvent(e); menu.show(); menu.focus(); }, null, menu );
edit = asset.edit = document.createElement('a');
edit.href = WebGUI.AssetManager.appendToUrl(
data.url, 'func=edit;proceed=manageAssets'
);
edit.appendChild(document.createTextNode(
WebGUI.AssetManager.i18n.get('Asset', 'edit')
));
more = asset.more = document.createElement('a');
more.href = '#';
more.appendChild(document.createTextNode(
WebGUI.AssetManager.i18n.get('Asset','More')
));
YAHOO.util.Event.addListener(
more, 'click', WebGUI.AssetManager.onMoreClick, asset
);
}
elCell.appendChild(asset.edit);
elCell.appendChild(asset.bar);
elCell.appendChild(asset.more);
};
/*---------------------------------------------------------------------------
@ -171,13 +187,20 @@ WebGUI.AssetManager.formatRank = function ( elCell, oRecord, oColumn, orderNumbe
+ 'onchange="WebGUI.AssetManager.selectRow( this )" />';
};
/*---------------------------------------------------------------------------
WebGUI.AssetManager.DefaultSortedBy ( )
WebGUI.AssetManager.onMoreClick ( event, asset )
Event handler for the more menu's click event
*/
WebGUI.AssetManager.DefaultSortedBy = {
"key" : "lineage",
"dir" : YAHOO.widget.DataTable.CLASS_ASC
WebGUI.AssetManager.onMoreClick = function (e, a) {
var options, menu = a.menu, d = a.data;
YAHOO.util.Event.stopEvent(e);
if (!menu) {
options = WebGUI.AssetManager.buildMoreMenu(d.url, a.more, d.actions);
a.menu = menu = new YAHOO.widget.Menu('assetMenu'+d.assetId, options);
menu.render(document.getElementById('assetManager'));
}
menu.show();
menu.focus();
};
/*---------------------------------------------------------------------------
@ -283,7 +306,9 @@ WebGUI.AssetManager.initDataTable = function (o) {
{ key: 'childCount' }
],
metaFields: {
totalRecords: "totalAssets" // Access to value in the server response
totalRecords : 'totalAssets',
sortColumn : 'sort',
sortDirection : 'dir'
}
};
@ -298,13 +323,19 @@ WebGUI.AssetManager.initDataTable = function (o) {
initialRequest : 'recordOffset=0',
dynamicData : true,
paginator : assetPaginator,
sortedBy : WebGUI.AssetManager.DefaultSortedBy,
generateRequest : WebGUI.AssetManager.BuildQueryString
}
);
WebGUI.AssetManager.DataTable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
oPayload.totalRecords = oResponse.meta.totalRecords;
var m = oResponse.meta;
oPayload.totalRecords = m.totalRecords;
this.set('sortedBy', {
key: m.sortColumn,
dir: m.sortDirection === 'desc' ?
YAHOO.widget.DataTable.CLASS_DESC :
YAHOO.widget.DataTable.CLASS_ASC
});
return oPayload;
};