Merge commit 'v7.10.21' into WebGUI8. Also, add POD and fix broken tests.
This commit is contained in:
commit
4855816a29
72 changed files with 1357 additions and 82 deletions
|
|
@ -1,3 +1,13 @@
|
|||
7.10.21
|
||||
- added #9668 extension template variable to attachment loops for the following assets:
|
||||
Article,Post,Event,File,Form::Attachments,Folder
|
||||
- added WaitForUserConfirmation workflow activity
|
||||
- added new setting - Enable Users after Anonymous Registration?
|
||||
- added the optional WebGUI::Content::PDFGenerator, not enabled by default
|
||||
(see the module's documentation).
|
||||
- fixed #12204: Default forum notification template produces invalid HTML
|
||||
- fixed #12202: JsonTable refers to unexistent YUI file
|
||||
|
||||
7.10.20
|
||||
- fixed: Do not call group methods on an undefined value.
|
||||
- fixed #12178: random deletion of columns may happen when a schema is saved (Amir Plivatsky)
|
||||
|
|
@ -13,6 +23,7 @@
|
|||
- fixed #12135: Geo::Coder::Googlev3 needs common sense
|
||||
- fixed #12183: Posts do not disqualify themselves when purged
|
||||
- fixed #12189: installClass ignores preload.custom
|
||||
- fixed #12197: Default date Thingy disables date
|
||||
|
||||
7.10.19
|
||||
- fixed #12169: extras uploads symlink export
|
||||
|
|
@ -4224,3 +4235,4 @@
|
|||
- Made the Include macro more secure.
|
||||
- Added Len's patch to fix some caching problems.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ save you many hours of grief.
|
|||
Account Macro template
|
||||
Admin Toggle Macro template
|
||||
|
||||
7.10.21
|
||||
--------------------------------------------------------------------
|
||||
* WebGUI now depends on Kwargs.
|
||||
|
||||
7.10.17
|
||||
--------------------------------------------------------------------
|
||||
* Due to a formatting problem with form variables in the PayPal driver, WebGUI
|
||||
|
|
|
|||
BIN
docs/upgrades/packages-7.10.21/default_forum_notification.wgpkg
Normal file
BIN
docs/upgrades/packages-7.10.21/default_forum_notification.wgpkg
Normal file
Binary file not shown.
146
docs/upgrades/upgrade_7.10.20-7.10.21.pl
Normal file
146
docs/upgrades/upgrade_7.10.20-7.10.21.pl
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#!/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.21';
|
||||
my $quiet; # this line required
|
||||
|
||||
|
||||
my $session = start(); # this line required
|
||||
addWaitForConfirmationWorkflow($session);
|
||||
addCreateUsersEnabledSetting($session);
|
||||
finish($session); # this line required
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
sub addWaitForConfirmationWorkflow {
|
||||
my $session = shift;
|
||||
my $c = $session->config;
|
||||
my $exists = $c->get('workflowActivities/WebGUI::User');
|
||||
my $class = 'WebGUI::Workflow::Activity::WaitForUserConfirmation';
|
||||
unless (grep { $_ eq $class } @$exists) {
|
||||
print "Adding WaitForUserConfirmation workflow..." unless $quiet;
|
||||
$c->addToArray('workflowActivities/WebGUI::User' => $class);
|
||||
print "Done!\n" unless $quiet;
|
||||
}
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
sub addCreateUsersEnabledSetting {
|
||||
my $session = shift;
|
||||
my $s = $session->setting;
|
||||
my $name = 'enableUsersAfterAnonymousRegistration';
|
||||
return if $s->has($name);
|
||||
print "Adding $name setting..." unless $quiet;
|
||||
$s->add($name => 1);
|
||||
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
|
||||
|
|
@ -945,6 +945,7 @@
|
|||
"WebGUI::Workflow::Activity::NotifyAboutUser",
|
||||
"WebGUI::Workflow::Activity::ActivateUser",
|
||||
"WebGUI::Workflow::Activity::DeactivateUser",
|
||||
"WebGUI::Workflow::Activity::WaitForUserConfirmation",
|
||||
"WebGUI::Workflow::Activity::DeleteUser"
|
||||
],
|
||||
"WebGUI::VersionTag" : [
|
||||
|
|
|
|||
|
|
@ -1126,10 +1126,12 @@ sub getEditForm {
|
|||
|
||||
###
|
||||
# Properties
|
||||
my $overrides = $session->config->get("assets/".$self->className) || {};
|
||||
foreach my $property ( $self->getProperties ) {
|
||||
my $fieldHash = $self->getFieldData( $property );
|
||||
next if $fieldHash->{noFormPost};
|
||||
|
||||
$fieldHash = $self->setupFormField($property, $fieldHash, $overrides);
|
||||
# Create tabs to have labels added later
|
||||
if ( !$f->getTab( $fieldHash->{tab} ) ) {
|
||||
$f->addTab( name => $fieldHash->{tab}, label => $fieldHash->{tab} );
|
||||
|
|
@ -1167,22 +1169,37 @@ sub getEditForm {
|
|||
return $f;
|
||||
} ## end sub getEditForm
|
||||
|
||||
=head2 setupFormField ( $fieldName, $fieldHash, $overrides )
|
||||
|
||||
Applies overrides from the WebGUI config file to a set of field data. The overridden
|
||||
and updated field data is returned.
|
||||
|
||||
=head3 $fieldName
|
||||
|
||||
The name of the field.
|
||||
|
||||
=head3 $fieldHash
|
||||
|
||||
A hash reference of field data for $fieldName.
|
||||
|
||||
=head3 $overrides
|
||||
|
||||
A hash reference of overrides from the config file. This is passed in instead of
|
||||
looking it up each time as a speed optimization.
|
||||
|
||||
=cut
|
||||
|
||||
sub setupFormField {
|
||||
my ( $self, $tabform, $fieldName, $extraFields, $overrides ) = @_;
|
||||
my %params = %{ $extraFields->{$fieldName} };
|
||||
my $tab = delete $params{tab};
|
||||
my ( $self, $fieldName, $fieldHash, $overrides ) = @_;
|
||||
|
||||
if ( exists $overrides->{fields}{$fieldName} ) {
|
||||
my %overrideParams = %{ $overrides->{fields}{$fieldName} };
|
||||
my $overrideTab = delete $overrideParams{tab};
|
||||
$tab = $overrideTab if defined $overrideTab;
|
||||
foreach my $key ( keys %overrideParams ) {
|
||||
$params{"-$key"} = $overrideParams{$key};
|
||||
}
|
||||
return $fieldHash unless exists $overrides->{fields}->{$fieldName};
|
||||
my %overrideParams = %{ $overrides->{fields}->{$fieldName} };
|
||||
foreach my $key ( keys %overrideParams ) {
|
||||
(my $canon = $key) =~ s/^-//;
|
||||
$fieldHash->{$canon} = $overrideParams{$key};
|
||||
}
|
||||
return $fieldHash;
|
||||
|
||||
$tab ||= 'properties';
|
||||
return $tabform->getTab($tab)->addField( delete $params{fieldType}, %params);
|
||||
} ## end sub setupFormField
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1289,6 +1289,7 @@ sub getTemplateVars {
|
|||
url => $storage->getUrl($filename),
|
||||
icon => $storage->getFileIconUrl($filename),
|
||||
filename => $filename,
|
||||
extension => WebGUI::Storage->getFileExtension($filename),
|
||||
thumbnail => $storage->getThumbnailUrl($filename),
|
||||
isImage => $storage->isImage($filename),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -622,6 +622,7 @@ sub view {
|
|||
$var{fileUrl} = $self->getFileUrl;
|
||||
$var{fileIcon} = $self->getFileIconUrl;
|
||||
$var{fileSize} = Number::Format::format_bytes($self->get("assetSize"));
|
||||
$var{extension} = WebGUI::Storage->getFileExtension( $self->get("filename"));
|
||||
my $out = $self->processTemplate(\%var,undef,$self->{_viewTemplate});
|
||||
if (!$self->session->isAdminOn && $self->get("cacheTimeout") > 10) {
|
||||
$self->session->cache->set($self->getViewCacheKey, $out, $self->get("cacheTimeout"));
|
||||
|
|
|
|||
|
|
@ -1092,6 +1092,7 @@ sub getTemplateVars {
|
|||
url => $fileUrl,
|
||||
icon => $storage->getFileIconUrl($filename),
|
||||
filename => $filename,
|
||||
extension => WebGUI::Storage->getFileExtension($filename),
|
||||
thumbnail => $isImage ? $storage->getThumbnailUrl($filename) : '',
|
||||
isImage => $isImage,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -340,6 +340,7 @@ sub view {
|
|||
}
|
||||
push(@{$var{attachment_loop}}, {
|
||||
filename => $file,
|
||||
extension => WebGUI::Storage->getFileExtension($file),
|
||||
isImage => $storage->isImage($file),
|
||||
url=> $storage->getUrl($file),
|
||||
thumbnailUrl => $storage->getThumbnailUrl($file),
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ sub view {
|
|||
"icon.small" => $child->getIcon(1),
|
||||
"icon.big" => $child->getIcon,
|
||||
type => $child->getName,
|
||||
extension => WebGUI::Storage->getFileExtension( $child->get("filename")),
|
||||
url => $child->getUrl,
|
||||
canEdit => $child->canEdit,
|
||||
controls => $child->getToolbar,
|
||||
|
|
|
|||
|
|
@ -635,6 +635,10 @@ sub editThingDataSave {
|
|||
if ($self->field_isa($fieldType, 'WebGUI::Form::File')) {
|
||||
$field->{ defaultValue } = $thingData{ "field_" . $field->{ fieldId } };
|
||||
}
|
||||
elsif ($fieldType eq 'Date' or $fieldType eq 'DateTime') { ##Must be in epoch format to be stored in the db.
|
||||
my $wdt = WebGUI::DateTime->new($session, $field->{defaultValue})->cloneToUserTimeZone;
|
||||
$field->{defaultValue} = $wdt->epoch;
|
||||
}
|
||||
$fieldValue = $thingData->{$fieldName} || $session->form->process($fieldName,$fieldType,$field->{defaultValue},$field);
|
||||
}
|
||||
if ($field->{status} eq "required" && ($fieldValue =~ /^\s$/x || $fieldValue eq "" || !(defined $fieldValue))) {
|
||||
|
|
@ -1042,12 +1046,12 @@ sub getFieldValue {
|
|||
|
||||
my $fieldType = lc $field->{fieldType};
|
||||
if ($fieldType eq "date"){
|
||||
my $dt = WebGUI::DateTime->new($session, $value);
|
||||
$processedValue = $dt->webguiDate($dateFormat);
|
||||
my $wdt = WebGUI::DateTime->new($session, $value);
|
||||
$processedValue = $wdt->cloneToUserTimeZone->webguiDate($dateFormat);
|
||||
}
|
||||
elsif ($fieldType eq "datetime"){
|
||||
my $dt = WebGUI::DateTime->new($session, $value);
|
||||
$processedValue = $dt->webguiDate($dateTimeFormat);
|
||||
my $wdt = WebGUI::DateTime->new($session, $value);
|
||||
$processedValue = $wdt->cloneToUserTimeZone->webguiDate($dateTimeFormat);
|
||||
}
|
||||
# TODO: The otherThing field type is probably also handled by getFormPlugin, so the elsif below can probably be
|
||||
# safely removed. However, this requires more testing than I can provide right now, so for now this stays the
|
||||
|
|
@ -3069,7 +3073,6 @@ sub www_exportThing {
|
|||
|
||||
### Loop through the returned structure and put it through Text::CSV
|
||||
# Column heads
|
||||
$self->session->log->warn("field labels: ". join ' ', @fieldLabels);
|
||||
my $csv_filename = 'export_'.$thingProperties->{label}.'.csv';
|
||||
open my $CSV, '>', $tempStorage->getPath($csv_filename);
|
||||
print $CSV WebGUI::Text::joinCSV( @fieldLabels );
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use WebGUI::User;
|
|||
use WebGUI::Form::Captcha;
|
||||
use WebGUI::Macro;
|
||||
use WebGUI::Deprecate;
|
||||
use Scope::Guard qw(guard);
|
||||
use Encode ();
|
||||
use Tie::IxHash;
|
||||
|
||||
|
|
@ -679,12 +680,28 @@ sub www_createAccountSave {
|
|||
$properties->{ identifier } = $self->hashPassword($password);
|
||||
$properties->{ passwordLastUpdated } = time();
|
||||
$properties->{ passwordTimeout } = $setting->get("webguiPasswordTimeout");
|
||||
$properties->{ status } = 'Deactivated' if ($setting->get("webguiValidateEmail"));
|
||||
|
||||
my $afterCreateMessage = $self->SUPER::createAccountSave($username,$properties,$password,$profile);
|
||||
|
||||
my $sendEmail = $setting->get('webguiValidateEmail');
|
||||
|
||||
# We need to deactivate the user and log him out if there are additional
|
||||
# things that need to be done before he should be logged in.
|
||||
my $cleanupUser;
|
||||
if ($sendEmail || !$setting->get('enableUsersAfterAnonymousRegistration')) {
|
||||
$cleanupUser = guard {
|
||||
$self->user->status('Deactivated');
|
||||
$session->var->end($session->var->get('sessionId'));
|
||||
$session->var->start(1, $session->getId);
|
||||
my $u = WebGUI::User->new($session, 1);
|
||||
$self->{user} = $u;
|
||||
$self->logout;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
# Send validation e-mail if required
|
||||
if ($setting->get("webguiValidateEmail")) {
|
||||
if ($sendEmail) {
|
||||
my $key = $session->id->generate;
|
||||
$self->update(emailValidationKey=>$key);
|
||||
my $mail = WebGUI::Mail::Send->create($self->session, {
|
||||
|
|
@ -700,12 +717,6 @@ WebGUI::Asset::Template->newById($self->session,$self->getSetting('accountActiva
|
|||
$mail->addText($text);
|
||||
$mail->addFooter;
|
||||
$mail->queue;
|
||||
$self->user->status("Deactivated");
|
||||
$session->end();
|
||||
$session->start(1, $session->getId);
|
||||
my $u = WebGUI::User->new($session, 1);
|
||||
$self->{user} = $u;
|
||||
$self->logout;
|
||||
return $self->www_displayLogin($i18n->get('check email for validation','AuthWebGUI'));
|
||||
}
|
||||
return $afterCreateMessage;
|
||||
|
|
|
|||
145
lib/WebGUI/Content/PDFGenerator.pm
Normal file
145
lib/WebGUI/Content/PDFGenerator.pm
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package WebGUI::Content::PDFGenerator;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use List::Util qw(first);
|
||||
use Scope::Guard qw(guard);
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Content::Asset;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Content::PDFGenerator
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Generates a PDF of the requested URL when op=generatePdf.
|
||||
|
||||
=head1 PREREQUISITES
|
||||
|
||||
This handler depends on wkpdftohtml, which does not ship with WebGUI and is,
|
||||
as of this writing, still in active development. This handler was written for
|
||||
version 0.9.9. It is available from http://code.google.com/p/wkhtmltopdf/.
|
||||
Compiling is rather difficult, but static binaries are available for the most
|
||||
popular platforms.
|
||||
|
||||
=head1 INSTALLATION
|
||||
|
||||
Enable this content handler in your WebGUI config file, placing it somewhere
|
||||
before WebGUI::Content::Operation, and add a pdfGen section to your config
|
||||
file at the top level. This must contain the path to your wkhtmltopdf
|
||||
executable, a cache timeout (how many seconds to cache the pdf), and
|
||||
optionally the userId of a user to view the page as (defaults to Visitor). It
|
||||
can also contain additional command line arguments to pass to wkhtmltopdf.
|
||||
|
||||
"pdfGen" : {
|
||||
"exe" : "/usr/local/bin/wkhtmltopdf",
|
||||
"args" : "--orientation Landscape",
|
||||
"userId" : "_f7d61hs6djh0fjnxqw21",
|
||||
"cacheTimeout" : 3600 # 1 hour cache timeout
|
||||
},
|
||||
"contentHandlers" : [
|
||||
#...
|
||||
"WebGUI::Content::PDFGenerator",
|
||||
#...
|
||||
"WebGUI::Content::Operation",
|
||||
#...
|
||||
"WebGUI::Content::NotFound"
|
||||
],
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Return the cached pdf, generating if necessary.
|
||||
|
||||
=head2 cache ($asset)
|
||||
|
||||
Returns the cached PDF for an asset, if necessary
|
||||
|
||||
=cut
|
||||
|
||||
sub cache {
|
||||
my $asset = shift;
|
||||
my $session = $asset->session;
|
||||
my $key = join '', 'PDFGen', $session->url->getRequestedUrl, $asset->get('revisionDate');
|
||||
my $cache = $session->cache();
|
||||
my $content = $cache->get($key);
|
||||
unless ($content) {
|
||||
$content = generate($asset);
|
||||
$cache->set($key, $content, $session->config->get('pdfGen/cacheTimeout'));
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Generate the pdf unconditionally and return it as a string.
|
||||
|
||||
=head2 generate ($asset)
|
||||
|
||||
Generate the pdf unconditionally and return it as a string.
|
||||
|
||||
=cut
|
||||
|
||||
sub generate {
|
||||
my $asset = shift;
|
||||
my $session = $asset->session;
|
||||
my $url = $session->url;
|
||||
my $c = $session->config;
|
||||
my $o = $c->get('pdfGen');
|
||||
my $login = WebGUI::Session->open($c->getWebguiRoot, $c->getFilename);
|
||||
my $guard = guard { $login->var->end; $login->close };
|
||||
$login->user({ userId => $o->{userId} || 1 });
|
||||
my @args = (
|
||||
$o->{exe}, @{$o->{args} || []},
|
||||
'--cookie', $c->get('cookieName'), $login->getId,
|
||||
$url->getSiteURL . $url->gateway($url->getRequestedUrl),
|
||||
'-'
|
||||
);
|
||||
|
||||
# We're using backticks because trying to run external programs from a
|
||||
# mod_perl process is extremely tricky any other way, but TODO: figure out
|
||||
# use a real call and pass an array of args, as that would be safer.
|
||||
my $cmd = join ' ', @args;
|
||||
`$cmd`;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cache ($asset)
|
||||
|
||||
Figure out which asset we need to check permissions for
|
||||
|
||||
=cut
|
||||
|
||||
sub getRequestedAsset {
|
||||
my $session = shift;
|
||||
my $assetUrl = $session->url->getRequestedUrl;
|
||||
my $perms = WebGUI::Content::Asset::getUrlPermutations($assetUrl);
|
||||
foreach my $url (@$perms) {
|
||||
if (my $asset = WebGUI::Content::Asset::getAsset($session, $url)) {
|
||||
return $asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Top-level handler.
|
||||
|
||||
=head2 handler ($session)
|
||||
|
||||
Top-level handler
|
||||
|
||||
=cut
|
||||
|
||||
sub handler {
|
||||
my $session = shift;
|
||||
my $op = $session->form->get('op');
|
||||
return undef unless $op && $op eq 'generatePdf';
|
||||
my $asset = getRequestedAsset($session);
|
||||
return $session->privilege->noAccess unless $asset->canView;
|
||||
$session->http->setMimeType('application/pdf');
|
||||
return cache($asset);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -300,6 +300,7 @@ sub www_upload {
|
|||
title => $filename,
|
||||
url => "attachments/".$filename,
|
||||
filename => $filename,
|
||||
extension => WebGUI::Storage->getFileExtension($filename),
|
||||
ownerUserId => $owner,
|
||||
groupIdEdit => "3",
|
||||
groupIdView => "7",
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ sub prepare {
|
|||
$style->setCss( $url->extras( 'yui/build/button/assets/skins/sam/button.css'));
|
||||
$style->setCss( $url->extras( 'yui/build/calendar/assets/skins/sam/calendar.css'));
|
||||
$style->setCss( $url->extras('yui/build/container/assets/skins/sam/container.css'));
|
||||
$style->setCss( $url->extras('yui-webgui/build/form/datatable.css'));
|
||||
$style->setScript( $url->extras('yui/build/container/container-min.js') );
|
||||
$style->setScript( $url->extras('yui/build/button/button-min.js') );
|
||||
$style->setScript( $url->extras('yui/build/calendar/calendar-min.js') );
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ Send JS required for this plugin.
|
|||
sub headTags {
|
||||
my $self = shift;
|
||||
my ( $url, $style ) = $self->session->quick(qw( url style ));
|
||||
$style->setScript( $url->extras('yui/build/connection/connection-min.js') );
|
||||
$style->setScript( $url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'));
|
||||
$style->setScript( $url->extras('yui/build/json/json-min.js'));
|
||||
$style->setScript( $url->extras('yui/build/connect/connect-min.js') );
|
||||
$style->setScript( $url->extras('yui-webgui/build/i18n/i18n.js') );
|
||||
$style->setScript( $url->extras('yui-webgui/build/form/jsontable.js'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ sub getOperations {
|
|||
'ajaxDeleteUser' => 'User',
|
||||
'ajaxUpdateUser' => 'User',
|
||||
'becomeUser' => 'User',
|
||||
'confirmUserEmail' => 'User',
|
||||
'deleteUser' => 'User',
|
||||
'editUser' => 'User',
|
||||
'editUserSave' => 'User',
|
||||
|
|
|
|||
|
|
@ -399,6 +399,15 @@ sub definition {
|
|||
hoverHelp=>$i18n->get('118 description'),
|
||||
defaultValue=>$setting->get("anonymousRegistration")
|
||||
});
|
||||
push(@fields, {
|
||||
tab => 'user',
|
||||
fieldType => 'yesNo',
|
||||
name => 'enableUsersAfterAnonymousRegistration',
|
||||
label => $i18n->get('Enable Users after Anonymous Registration?'),
|
||||
hoverHelp => $i18n->get('enableUsersAfterAnonymousRegistration help'),
|
||||
defaultValue => $setting->get('enableUsersAfterAnonymousRegistration')
|
||||
}
|
||||
);
|
||||
push(@fields, {
|
||||
tab=>"user",
|
||||
fieldType=>"yesNo",
|
||||
|
|
|
|||
|
|
@ -415,6 +415,38 @@ sub www_ajaxCreateUser {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_confirmUserEmail ( )
|
||||
|
||||
Process links clicked from mails sent out by the WaitForUserConfmration
|
||||
workflow activity.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_confirmUserEmail {
|
||||
my $session = shift;
|
||||
my $f = $session->form;
|
||||
my $instanceId = $f->get('instanceId');
|
||||
my $token = $f->get('token');
|
||||
my $actId = $f->get('activityId');
|
||||
my $activity = WebGUI::Workflow::Activity->new($session, $actId)
|
||||
or die;
|
||||
my $instance = WebGUI::Workflow::Instance->new($session, $instanceId)
|
||||
or die;
|
||||
if ($activity->confirm($instance, $token)) {
|
||||
my $msg = $activity->get('okMessage');
|
||||
unless ($msg) {
|
||||
my $i18n = WebGUI::International->new($session, 'WebGUI');
|
||||
$msg = $i18n->get('ok');
|
||||
}
|
||||
return $session->style->userStyle($msg);
|
||||
}
|
||||
else {
|
||||
return $session->privilege->noAccess;
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_ajaxDeleteUser ( )
|
||||
|
||||
Delete a user using a web service.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ Package WebGUI::Test::MockAsset
|
|||
=head1 DESCRIPTION
|
||||
|
||||
Creates fake WebGUI::Asset objects and sets them up to be returned by WebGUI::Asset's normal constructors.
|
||||
Most of the time, you'll be mocking templates to read which variables are sent to their
|
||||
templates. Here's how to do that:
|
||||
|
||||
my $mockAsset = WebGUI::Test::MockAsset->new('WebGUI::Asset::Template');
|
||||
$mockAsset->mock_id($template_id_to_mock);
|
||||
my $templateVars;
|
||||
$templateMock->mock('process', sub { $templateVars = $_[1]; } );
|
||||
$templateMock->set_true('prepare', sub { } );
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,29 @@ These methods are available from this class:
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 changeWorkflow ( $workflowId, $instance, $skipDelete )
|
||||
|
||||
Kicks a new workflow in a new instance with the same object the current
|
||||
instance has, deleting the old instance unless you say otherwise.
|
||||
|
||||
=cut
|
||||
|
||||
sub changeWorkflow {
|
||||
my ($self, $workflowId, $instance, $skipDelete) = @_;
|
||||
WebGUI::Workflow::Instance->create(
|
||||
$self->session, {
|
||||
workflowId => $workflowId,
|
||||
methodName => $instance->get('methodName'),
|
||||
className => $instance->get('className'),
|
||||
parameters => $instance->get('parameters'),
|
||||
priority => $instance->get('priority'),
|
||||
}
|
||||
)->start(1);
|
||||
$instance->delete() unless $skipDelete;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cleanup ( )
|
||||
|
||||
Override this activity to add a cleanup routine to be run if an instance
|
||||
|
|
|
|||
270
lib/WebGUI/Workflow/Activity/WaitForUserConfirmation.pm
Normal file
270
lib/WebGUI/Workflow/Activity/WaitForUserConfirmation.pm
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
package WebGUI::Workflow::Activity::WaitForUserConfirmation;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use base 'WebGUI::Workflow::Activity';
|
||||
use WebGUI::Asset::Template;
|
||||
use WebGUI::International;
|
||||
use WebGUI::Inbox::Message;
|
||||
use WebGUI::Macro;
|
||||
use Kwargs;
|
||||
use Tie::IxHash;
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 confirm ( $instance, $token )
|
||||
|
||||
Returns true (and sets the workflow as done) if the token matches the one we
|
||||
generated for the email.
|
||||
|
||||
=cut
|
||||
|
||||
sub confirm {
|
||||
my ($self, $instance, $token) = @_;
|
||||
my $id = $self->getId;
|
||||
return 0 unless $token eq $instance->getScratch("$id-token");
|
||||
$instance->setScratch("$id-status", 'done');
|
||||
return 1;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 definition ( )
|
||||
|
||||
See WebGUI::Workflow::Activity::definition for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub definition {
|
||||
my ($class, $session, $def) = @_;
|
||||
my $i18n = WebGUI::International->new(
|
||||
$session, 'Activity_WaitForUserConfirmation'
|
||||
);
|
||||
|
||||
tie my %props, 'Tie::IxHash', (
|
||||
emailFrom => {
|
||||
fieldType => 'user',
|
||||
defaultValue => 3,
|
||||
},
|
||||
emailSubject => {
|
||||
fieldType => 'text',
|
||||
defaultValue => 'Confirmation Email',
|
||||
},
|
||||
template => {
|
||||
fieldType => 'textarea',
|
||||
defaultValue => $i18n->get('your template goes here'),
|
||||
},
|
||||
templateParser => {
|
||||
fieldType => 'templateParser',
|
||||
defaultValue => $session->config->get('defaultTemplateParser'),
|
||||
},
|
||||
okMessage => {
|
||||
fieldType => 'HTMLArea',
|
||||
},
|
||||
waitBetween => {
|
||||
fieldType => 'interval',
|
||||
defaultValue => 60*5
|
||||
},
|
||||
expireAfter => {
|
||||
fieldType => 'interval',
|
||||
defaultValue => 60*60*24*7,
|
||||
},
|
||||
doOnExpire => {
|
||||
fieldType => 'workflow',
|
||||
type => 'WebGUI::User',
|
||||
none => 1,
|
||||
}
|
||||
);
|
||||
|
||||
for my $key (keys %props) {
|
||||
$props{$key}{label} = $i18n->get("$key label");
|
||||
$props{$key}{hoverHelp} = $i18n->get("$key hoverHelp");
|
||||
}
|
||||
|
||||
push @$def, {
|
||||
name => $i18n->get('topicName'),
|
||||
properties => \%props,
|
||||
};
|
||||
|
||||
return $class->SUPER::definition( $session, $def );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 execute ( )
|
||||
|
||||
See WebGUI::Workflow::Activity::execute for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub execute {
|
||||
my ($self, $object, $instance) = @_;
|
||||
my $id = $self->getId;
|
||||
my $statk = "$id-status";
|
||||
my $start = "$id-started";
|
||||
my $status = $instance->getScratch($statk);
|
||||
my $subject = $self->get('emailSubject');
|
||||
my $parser = $self->get('templateParser');
|
||||
WebGUI::Macro::process(\$subject);
|
||||
my $body = WebGUI::Asset::Template->processRaw(
|
||||
$self->session,
|
||||
$self->get('template'),
|
||||
$self->getTemplateVariables($object, $instance),
|
||||
$parser,
|
||||
);
|
||||
WebGUI::Macro::process(\$body);
|
||||
unless ($status) {
|
||||
$instance->setScratch($start => $self->now);
|
||||
$self->sendEmail(
|
||||
from => $self->get('emailFrom'),
|
||||
to => $object->userId,
|
||||
subject => $subject,
|
||||
body => $body,
|
||||
);
|
||||
$instance->setScratch($statk => 'waiting');
|
||||
return $self->wait;
|
||||
}
|
||||
return $self->COMPLETE if $status eq 'done' || $status eq 'expired';
|
||||
if ($status eq 'waiting') {
|
||||
my $end = $instance->getScratch($start) + $self->get('expireAfter');
|
||||
if ($self->now > $end) {
|
||||
$self->expire($instance);
|
||||
$instance->setScratch($statk => 'expired');
|
||||
return $self->COMPLETE;
|
||||
}
|
||||
return $self->wait;
|
||||
}
|
||||
$self->session->log->error("Unknown status: $status");
|
||||
return $self->ERROR;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 expire ( $instance )
|
||||
|
||||
Deletes the workflow instance and kicks off a configured workflow if there is
|
||||
one.
|
||||
|
||||
=cut
|
||||
|
||||
sub expire {
|
||||
my ($self, $instance) = @_;
|
||||
if (my $id = $self->get('doOnExpire')) {
|
||||
$self->changeWorkflow($id, $instance);
|
||||
}
|
||||
else {
|
||||
$instance->delete();
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getTemplateVariables ( $object, $instance )
|
||||
|
||||
Returns the variables to be used in rendering the email template.
|
||||
|
||||
=cut
|
||||
|
||||
sub getTemplateVariables {
|
||||
my ($self, $object, $instance) = @_;
|
||||
|
||||
my $user = $object->get;
|
||||
|
||||
# Kill all humans. I means references. Currently there seems to be a bug
|
||||
# in _rewriteVars in some of the template plugins that disallows us from
|
||||
# using arrayrefs with just strings in them, which is a common occurrence
|
||||
# in profile fields. When that bug gets fixed, we can (and should) take
|
||||
# this out.
|
||||
delete @{$user}{grep {ref $user->{$_} } keys %$user};
|
||||
|
||||
return {
|
||||
user => $user,
|
||||
link => $self->link($instance),
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 link ( $instance )
|
||||
|
||||
Returns the URL that needs to be visited by the user.
|
||||
|
||||
=cut
|
||||
|
||||
sub link {
|
||||
my ($self, $instance) = @_;
|
||||
my $url = $self->session->url;
|
||||
my $aid = $self->getId;
|
||||
my $iid = $instance->getId;
|
||||
my $token = $instance->getScratch("$aid-token");
|
||||
$instance->setScratch("$aid-token", $token = $self->token) unless $token;
|
||||
my $path = $url->page(
|
||||
"op=confirmUserEmail;instanceId=$iid;token=$token;activityId=$aid"
|
||||
);
|
||||
return $url->getSiteURL . $url->gateway($path);
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 now ( )
|
||||
|
||||
Just returns the current time, nice for testing.
|
||||
|
||||
=cut
|
||||
|
||||
sub now { time }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 sendEmail ( { from, to, subject, body } )
|
||||
|
||||
Takes a user to send email from, to, with a subject and a body all as
|
||||
keywords. Mostly here for testing, it just calls
|
||||
WebGUI::Inbox::Message->create() with proper arguments. 'from' and 'to' are
|
||||
userIds, not user objects.
|
||||
|
||||
=cut
|
||||
|
||||
sub sendEmail {
|
||||
my ($self, $from, $to, $subject, $body) = kwn @_, 1,
|
||||
qw(from to subject body);
|
||||
|
||||
WebGUI::Inbox::Message->create(
|
||||
$self->session, {
|
||||
message => $body,
|
||||
subject => $subject,
|
||||
status => 'pending',
|
||||
userId => $to,
|
||||
sentBy => $from,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 token ( )
|
||||
|
||||
Returns a random string to use as a token in the confirmation link
|
||||
|
||||
=cut
|
||||
|
||||
sub token {
|
||||
my $self = shift;
|
||||
$self->session->id->generate;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 wait ( )
|
||||
|
||||
Waits for the configured waitBetween interval.
|
||||
|
||||
=cut
|
||||
|
||||
sub wait {
|
||||
my $self = shift;
|
||||
return $self->WAITING($self->get('waitBetween'));
|
||||
}
|
||||
|
||||
1;
|
||||
74
lib/WebGUI/i18n/English/Activity_WaitForUserConfirmation.pm
Normal file
74
lib/WebGUI/i18n/English/Activity_WaitForUserConfirmation.pm
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package WebGUI::i18n::English::Activity_WaitForUserConfirmation;
|
||||
|
||||
use strict;
|
||||
|
||||
our $I18N = {
|
||||
'doOnExpire hoverHelp' => {
|
||||
message => q{Workflow to run after the waiting period has expired.},
|
||||
lastUpdated => 1311365415,
|
||||
},
|
||||
'doOnExpire label' => {
|
||||
message => q{Do On Expire},
|
||||
lastUpdated => 1311365395,
|
||||
},
|
||||
'emailFrom hoverHelp' => {
|
||||
message => q{Which user should the confirmation email be from?},
|
||||
lastUpdated => 1311363981,
|
||||
},
|
||||
'emailFrom label' => {
|
||||
message => q{Email From},
|
||||
lastUpdated => 1311363958,
|
||||
},
|
||||
'emailSubject label' => {
|
||||
message => q{Email Subject},
|
||||
lastUpdated => 1311363994,
|
||||
},
|
||||
'expireAfter hoverHelp' => {
|
||||
message => q{How long should we wait for the user to respond?},
|
||||
lastUpdated => 1311363900,
|
||||
},
|
||||
'expireAfter label' => {
|
||||
message => q{Expire After},
|
||||
lastUpdated => 1311363885,
|
||||
},
|
||||
'okMessage label' => {
|
||||
message => q{Confirmation Message},
|
||||
lastUpdated => 1311612584,
|
||||
},
|
||||
'okMessage hoverHelp' => {
|
||||
message => q{Message to display to the user when he clicks the confirm link},
|
||||
lastUpdated => 1311612632,
|
||||
},
|
||||
'template hoverHelp' => {
|
||||
message => q{Raw template code for the body of the email goes here.},
|
||||
lastUpdated => 1311364201,
|
||||
},
|
||||
'template label' => {
|
||||
message => q{Template},
|
||||
lastUpdated => 1311364181,
|
||||
},
|
||||
'templateParser label' => {
|
||||
message => q{Template Parser},
|
||||
lastUpdated => 1311364015,
|
||||
},
|
||||
'topicName' => {
|
||||
message => q{Wait For User Confirmation},
|
||||
lastUpdated => 1311364913,
|
||||
},
|
||||
'waitBetween hoverHelp' => {
|
||||
message => q{How long should we wait in between checks to see if the user has clicked the link?},
|
||||
lastUpdated => 1311363934,
|
||||
},
|
||||
'waitBetween label' => {
|
||||
message => q{Wait Interval},
|
||||
lastUpdated => 1311363920,
|
||||
},
|
||||
'your template goes here' => {
|
||||
message => q{Your template goes here!},
|
||||
lastUpdated => 1311365274,
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
|
||||
#vim:ft=perl
|
||||
|
|
@ -74,6 +74,18 @@ our $I18N = {
|
|||
context => q{Format for a column with a date},
|
||||
},
|
||||
|
||||
"format textarea" => {
|
||||
message => q{Textarea},
|
||||
lastUpdated => 0,
|
||||
context => q{Format for a textarea column},
|
||||
},
|
||||
|
||||
"format htmlarea" => {
|
||||
message => q{HTMLarea},
|
||||
lastUpdated => 0,
|
||||
context => q{Format for an HTMLarea column},
|
||||
},
|
||||
|
||||
"add column" => {
|
||||
message => q{Add Column},
|
||||
lastUpdated => 0,
|
||||
|
|
@ -152,6 +164,23 @@ our $I18N = {
|
|||
context => q{The name of a newly added value to a column},
|
||||
},
|
||||
|
||||
"data error" => {
|
||||
message => q{Data error.},
|
||||
lastUpdated => 0,
|
||||
context => q{Message to display when DataTable has data error},
|
||||
},
|
||||
|
||||
"sort ascending" => {
|
||||
message => q{Click to sort ascending},
|
||||
lastUpdated => 0,
|
||||
context => q{Message to display in tooltip to sort Column in ascending order},
|
||||
},
|
||||
|
||||
"sort descending" => {
|
||||
message => q{Click to sort descending},
|
||||
lastUpdated => 0,
|
||||
context => q{Message to display in tooltip to sort Column in descending order},
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -4776,6 +4776,18 @@ Users may override this setting in their profile.
|
|||
context => 'Choose, as in to select from a set of options',
|
||||
},
|
||||
|
||||
'Enable Users after Anonymous Registration?' => {
|
||||
message => 'Enable Users after Anonymous Registration?',
|
||||
lastUpdated => 1311618346,
|
||||
},
|
||||
|
||||
'enableUsersAfterAnonymousRegistration help' => {
|
||||
message => 'If this is off, '
|
||||
. 'users must be manually activated by a workflow or an admin.',
|
||||
lastUpdated => 1311618419,
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ checkModule('Starman', '0.2010', 2);
|
|||
checkModule('App::Cmd', '0.311' );
|
||||
checkModule('Devel::StackTrace', '1.27' );
|
||||
checkModule('Devel::StackTrace::WithLexicals', '0.03' );
|
||||
checkModule('Kwargs', );
|
||||
checkModule('Data::ICal', '0.16' );
|
||||
checkModule('common::sense', '3.2' );
|
||||
checkModule('Geo::Coder::Googlev3', '0.07' );
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -162,6 +162,11 @@ $storage->addFileFromFilesystem(WebGUI::Test->getTestCollateralPath('lamp.jpg'))
|
|||
$storage->addFileFromFilesystem(WebGUI::Test->getTestCollateralPath('littleTextFile'));
|
||||
my $attachment_loop = $post1->getTemplateVars()->{attachment_loop};
|
||||
|
||||
use Data::Dumper;
|
||||
diag Dumper($attachment_loop);
|
||||
|
||||
my @extensions = map { [ $_->{filename}, $_->{extension} ] } @{ $attachment_loop };
|
||||
|
||||
cmp_bag(
|
||||
$attachment_loop,
|
||||
[
|
||||
|
|
@ -170,6 +175,7 @@ cmp_bag(
|
|||
url => $storage->getUrl('gooey.jpg'),
|
||||
icon => $session->url->extras('fileIcons/jpg.gif'),
|
||||
thumbnail => $storage->getThumbnailUrl('gooey.jpg'),
|
||||
extension => 'jpg',
|
||||
isImage => bool(1),
|
||||
},
|
||||
{
|
||||
|
|
@ -177,6 +183,7 @@ cmp_bag(
|
|||
url => $storage->getUrl('lamp.jpg'),
|
||||
icon => $session->url->extras('fileIcons/jpg.gif'),
|
||||
thumbnail => $storage->getThumbnailUrl('lamp.jpg'),
|
||||
extension => 'jpg',
|
||||
isImage => bool(1),
|
||||
},
|
||||
{
|
||||
|
|
@ -184,6 +191,7 @@ cmp_bag(
|
|||
url => $storage->getUrl('littleTextFile'),
|
||||
icon => $session->url->extras('fileIcons/unknown.gif'),
|
||||
thumbnail => '',
|
||||
extension => undef,
|
||||
isImage => bool(0),
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ use File::Spec;
|
|||
##The goal of this test is to test the creation of Article Wobjects.
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Test::MockAsset;
|
||||
use WebGUI::Session;
|
||||
use Test::More tests => 23; # increment this value for each test you create
|
||||
use Test::More tests => 24; # increment this value for each test you create
|
||||
use Test::Deep;
|
||||
use Data::Dumper;
|
||||
use WebGUI::Asset::Wobject::Article;
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
|
@ -61,9 +64,10 @@ foreach my $newSetting (keys %{$newArticleSettings}) {
|
|||
}
|
||||
|
||||
# Test the duplicate method... not for assets, just the extended duplicate functionality of the article wobject
|
||||
my $filename = "page_title.jpg";
|
||||
my $filename = "extensions.tar";
|
||||
my $pathedFile = WebGUI::Test->getTestCollateralPath($filename);
|
||||
|
||||
|
||||
# Use some test collateral to create a storage location and assign it to our article
|
||||
my $storage = WebGUI::Storage->create($session);
|
||||
WebGUI::Test->addToCleanup($storage);
|
||||
|
|
@ -75,6 +79,10 @@ diag(join("\n", @{ $storage->getErrors })) unless $filenameOK;
|
|||
$article->update({storageId=>$storage->getId});
|
||||
my $storageOK = is($article->get('storageId'), $storage->getId, 'correct storage id stored');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip 'storage test setup problem', 3 unless $filenameOK and $storageOK;
|
||||
|
|
@ -117,6 +125,55 @@ $cachedOutput = $session->cache->get('view_'.$article->getId); # Check cache po
|
|||
isnt ($output, $cachedOutput, 'purgeCache method deletes cache');
|
||||
|
||||
|
||||
# lets test that our new template variable for the fileloop in the main view method returns the
|
||||
# right values for the new field in the attached files loop: <tmpl_var extension>
|
||||
# first we create a new template with only the <tmpl_var extension> field in it
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
my $templateId = 'DUMMY_TEMPLATE________';
|
||||
|
||||
my $templateMock = WebGUI::Test::MockAsset->new('WebGUI::Asset::Template');
|
||||
$templateMock->mock_id($templateId);
|
||||
my $templateVars;
|
||||
$templateMock->set_true('prepare', sub { } );
|
||||
$templateMock->mock('process', sub { $templateVars = $_[1]; } );
|
||||
|
||||
my @extTestFiles = ("rotation_test.png","littleTextFile","jquery.js","tooWide.gif");
|
||||
|
||||
foreach my $f (@extTestFiles) {
|
||||
my $pathedFile = WebGUI::Test->getTestCollateralPath($f);
|
||||
my $storedFilename = $storage->addFileFromFilesystem($pathedFile);
|
||||
}
|
||||
|
||||
$article->update({templateId=>$templateId});
|
||||
$article->prepareView;
|
||||
$article->view;
|
||||
|
||||
cmp_bag(
|
||||
$templateVars->{attachment_loop},
|
||||
[
|
||||
superhashof({
|
||||
filename => 'rotation_test.png',
|
||||
extension => 'png',
|
||||
}),
|
||||
superhashof({
|
||||
filename => 'littleTextFile',
|
||||
extension => undef,
|
||||
}),
|
||||
superhashof({
|
||||
filename => 'jquery.js',
|
||||
extension => 'js',
|
||||
}),
|
||||
superhashof({
|
||||
filename => 'tooWide.gif',
|
||||
extension => 'gif',
|
||||
}),
|
||||
superhashof({
|
||||
filename => 'extensions.tar',
|
||||
extension => 'tar',
|
||||
}),
|
||||
],
|
||||
) or diag Dumper($templateVars->{attachment_loop});
|
||||
|
||||
TODO: {
|
||||
local $TODO = "Tests to make later";
|
||||
ok(0, 'Test exportAssetData method');
|
||||
|
|
@ -126,3 +183,7 @@ TODO: {
|
|||
ok(0, 'Test www_deleteFile method');
|
||||
ok(0, 'Test www_view method... maybe?');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81,4 +81,4 @@ my $field1 = $thingy->getFields($thingId)->hashRef;
|
|||
|
||||
note 'getFieldValue';
|
||||
is $thingy->getFieldValue(WebGUI::Test->webguiBirthday, $field1), '8/16/2001', 'with epoch as default';
|
||||
is $thingy->getFieldValue('2011-07-04', $field1), '7/4/2011', 'with mysql date as default';
|
||||
is $thingy->getFieldValue('2011-07-04', $field1), '7/3/2011', 'with mysql date as default';
|
||||
|
|
|
|||
43
t/Asset/editFormOverride.t
Normal file
43
t/Asset/editFormOverride.t
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use warnings;
|
||||
use strict;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use lib "$FindBin::Bin/../t/lib";
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Asset;
|
||||
use Test::More tests => 2;
|
||||
use Monkey::Patch qw(patch_class);
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
WebGUI::Test->originalConfig('assets/WebGUI::Asset::Wobject::Layout');
|
||||
my $asset = WebGUI::Asset->getTempspace($session)->addChild(
|
||||
{
|
||||
className => 'WebGUI::Asset::Wobject::Layout',
|
||||
}
|
||||
);
|
||||
WebGUI::Test->addToCleanup($asset);
|
||||
|
||||
sub capture {
|
||||
my $save;
|
||||
my $patch = patch_class 'WebGUI::Form::Control' => new => sub {
|
||||
my $orig = shift;
|
||||
my $self = $orig->(@_);
|
||||
my $name = $self->get('name');
|
||||
$save = $self if $name && $name eq 'assetsToHide';
|
||||
return $self;
|
||||
};
|
||||
$asset->getEditForm;
|
||||
#use Data::Dumper::Concise;
|
||||
#print STDERR '# ' . Dumper $save->{_params};
|
||||
return $save;
|
||||
}
|
||||
|
||||
my $config = $session->config;
|
||||
my $pfx = 'assets/WebGUI::Asset::Wobject::Layout/fields/assetsToHide';
|
||||
$config->set("$pfx/uiLevel", 1);
|
||||
is capture->get('uiLevel'), 1, 'uiLevel override to 1';
|
||||
|
||||
$config->set("$pfx/uiLevel", "2");
|
||||
is capture->get('uiLevel'), 2, 'uiLEvel override to 2';
|
||||
|
|
@ -42,7 +42,7 @@ sub setSiteVersionTagMode {
|
|||
sub setUserVersionTagMode {
|
||||
my ($user, $newMode) = @_;
|
||||
|
||||
$user->profileField(q{versionTagMode}, $newMode);
|
||||
$user->update(versionTagMode => $newMode);
|
||||
|
||||
return;
|
||||
} #setUserVersionTagMode
|
||||
|
|
@ -158,10 +158,16 @@ ok(!defined $tagAgain2, 'nonexistent tag cannot be instantiated');
|
|||
$tag2->rollback;
|
||||
($tag, $tagAgain1, $tag2, $tagAgain2) = ();
|
||||
|
||||
my $master_tag = WebGUI::VersionTag->getWorking($session);
|
||||
my $node = WebGUI::Test->asset;
|
||||
$master_tag->commit;
|
||||
$node = $node->cloneFromDb;
|
||||
WebGUI::Test->addToCleanup($master_tag);
|
||||
|
||||
my $tag3 = WebGUI::VersionTag->create($session, {});
|
||||
$tag3->setWorking;
|
||||
my $asset1 = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset2 = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset1 = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset2 = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
is($tag3->getAssetCount, 2, 'tag with two assets');
|
||||
is($tag3->getRevisionCount, 2, 'tag with two revisions');
|
||||
$asset1 = $asset1->addRevision({ title => 'revised once', }, time+10);
|
||||
|
|
@ -171,7 +177,7 @@ is($tag3->getRevisionCount, 5, 'tag with five revisions');
|
|||
|
||||
my $tag4 = WebGUI::VersionTag->create($session, {});
|
||||
$tag4->setWorking;
|
||||
my $asset3 = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset3 = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
is($tag4->getAssetCount, 1, 'other tag with one asset');
|
||||
is($tag4->getRevisionCount, 1, 'other tag with one revision');
|
||||
$asset3->addRevision({ title => 'again revised once', }, time+40);
|
||||
|
|
@ -186,7 +192,7 @@ $tag4->rollback;
|
|||
#Test commitAsUser
|
||||
my $tag5 = WebGUI::VersionTag->create($session, {});
|
||||
$tag5->setWorking;
|
||||
my $asset5 = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset5 = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
is($tag5->get("createdBy"),1,'tag created by visitor');
|
||||
$tag5->commitAsUser(3);
|
||||
$tag5 = WebGUI::VersionTag->new($session, $tag5->getId); #Get the tag again - properties have changed
|
||||
|
|
@ -197,7 +203,7 @@ $tag5->rollback;
|
|||
#Test commitAsUser with options
|
||||
my $tag6 = WebGUI::VersionTag->create($session, {});
|
||||
$tag6->setWorking;
|
||||
my $asset6 = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset6 = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
$tag6->commitAsUser(3, { commitNow => "yes" });
|
||||
$tag6 = WebGUI::VersionTag->new($session, $tag6->getId); #Get the tag again - properties have changed
|
||||
is($tag6->get("committedBy"),3,'tag committed by admin again');
|
||||
|
|
@ -253,7 +259,7 @@ ok($siteWideTag->getId() ne $userTagId, 'versionTagMode siteWide: siteWide tag h
|
|||
|
||||
$siteWideTag->clearWorking();
|
||||
|
||||
my $asset4 = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet' });
|
||||
my $asset4 = $node->addChild({ className => 'WebGUI::Asset::Snippet' });
|
||||
|
||||
ok(defined ($siteWideTag = getWorking(1)), 'versionTagMode siteWide: reclaim version tag after clearWorking and addding new asset');
|
||||
|
||||
|
|
@ -321,7 +327,7 @@ $siteWideTag->rollback();
|
|||
setUserVersionTagMode($user, q{singlePerUser});
|
||||
my $tag = WebGUI::VersionTag->create($session, {});
|
||||
$tag->setWorking;
|
||||
my $asset = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
my $asset = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
is($tag->getAssetCount, 1, qq{$test_prefix [singlePerUser] tag with 1 asset});
|
||||
|
||||
# create admin session
|
||||
|
|
@ -372,7 +378,7 @@ $siteWideTag->rollback();
|
|||
setUserVersionTagMode($user, q{siteWide});
|
||||
$tag = WebGUI::VersionTag->create($session, {});
|
||||
$tag->setWorking;
|
||||
$asset = WebGUI::Test->asset->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
$asset = $node->addChild({ className => 'WebGUI::Asset::Snippet', });
|
||||
is($tag->getAssetCount, 1, qq{$test_prefix [siteWide] tag with 1 asset});
|
||||
|
||||
# create admin session
|
||||
|
|
|
|||
110
t/Workflow/Activity/WaitForUserConfirmation.t
Normal file
110
t/Workflow/Activity/WaitForUserConfirmation.t
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use warnings;
|
||||
use strict;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
use lib "$FindBin::Bin/../../t/lib";
|
||||
|
||||
use WebGUI::Test;
|
||||
use Test::More tests => 28;
|
||||
use Test::MockObject;
|
||||
use Test::MockObject::Extends;
|
||||
use WebGUI::Workflow::Activity;
|
||||
use Kwargs;
|
||||
use URI;
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
my $act = WebGUI::Workflow::Activity->newByPropertyHashRef(
|
||||
$session, {
|
||||
className => 'WebGUI::Workflow::Activity::WaitForUserConfirmation',
|
||||
activityId => 'test-activity',
|
||||
expireAfter => 60*60*24,
|
||||
waitBetween => 60*5,
|
||||
emailFrom => 3,
|
||||
emailSubject => 'Confirmation Email',
|
||||
templateParser => 'WebGUI::Asset::Template::TemplateToolkit',
|
||||
template => 'Hey [% user.firstName %] [% user.lastName %], '
|
||||
. 'click $link!',
|
||||
}
|
||||
);
|
||||
|
||||
is $act->wait, $act->WAITING(60*5), 'wait helper method';
|
||||
|
||||
$act = Test::MockObject::Extends->new($act);
|
||||
|
||||
my (%scratch, %profile);
|
||||
%profile = (
|
||||
email => 'target@test.com',
|
||||
firstName => 'Target',
|
||||
lastName => 'Targetson',
|
||||
);
|
||||
|
||||
my $user = Test::MockObject->new
|
||||
->mock(get => sub { \%profile })
|
||||
->mock(userId => sub { 'test-user-id' });
|
||||
|
||||
my $workflow = Test::MockObject->new
|
||||
->mock(setScratch => sub { $scratch{$_[1]} = $_[2] })
|
||||
->mock(getScratch => sub { $scratch{$_[1]} })
|
||||
->mock(getId => sub { 'test-workflow' });
|
||||
|
||||
my ($expired, $sent) = (0,0);
|
||||
$act->mock(sendEmail => sub { $sent++ })
|
||||
->mock(expire => sub { $expired++ })
|
||||
->mock(now => sub { 100 })
|
||||
->mock(token => sub { 'test-token' });
|
||||
|
||||
my $st = 'test-activity-status';
|
||||
|
||||
sub ex { $act->execute($user, $workflow) }
|
||||
sub clr {
|
||||
delete @scratch{'test-activity-started', $st};
|
||||
$sent = 0;
|
||||
$expired = 0;
|
||||
}
|
||||
|
||||
is ex, $act->wait, 'from scratch returns waiting';
|
||||
is $sent, 1, 'one email sent';
|
||||
is $scratch{$st}, 'waiting', 'scratch is waiting';
|
||||
is $scratch{'test-activity-started'}, 100, 'started at mocked time';
|
||||
is ex, $act->wait, 'still waiting';
|
||||
is $sent, 1, 'did not send second email';
|
||||
is $scratch{$st}, 'waiting', 'scratch still waiting';
|
||||
$scratch{$st} = 'done';
|
||||
is ex, $act->COMPLETE, 'returns complete after done';
|
||||
is ex, $act->COMPLETE, 'forever';
|
||||
is $expired, 0, 'not expired though';
|
||||
clr;
|
||||
is $act->execute($user, $workflow), $act->wait, 'waiting after clear';
|
||||
is $sent, 1, 'one email sent';
|
||||
$act->mock(now => sub { 60*60*24+101 });
|
||||
is ex, $act->COMPLETE, 'complete after expired';
|
||||
is $scratch{$st}, 'expired', 'expired status';
|
||||
is $expired, 1, 'expire called';
|
||||
|
||||
clr;
|
||||
my ($self, $to, $from, $subject, $body);
|
||||
$act->mock(
|
||||
sendEmail => sub {
|
||||
($self, $to, $from, $subject, $body) = kwn @_, 1,
|
||||
qw(to from subject body);
|
||||
}
|
||||
);
|
||||
ex;
|
||||
is $to, $user->userId, 'to';
|
||||
is $from, 3, 'from';
|
||||
is $subject, 'Confirmation Email', 'subject';
|
||||
my $link = URI->new($act->link($workflow));
|
||||
my %p = $link->query_form;
|
||||
is $body, "Hey Target Targetson, click $link!", 'body';
|
||||
is $p{token}, 'test-token', 'token in link';
|
||||
is $p{instanceId}, 'test-workflow', 'instance id in link';
|
||||
is $p{activityId}, 'test-activity', 'activity id in link';
|
||||
$act->unmock('token');
|
||||
is $act->link($workflow), $link, 'token only generated once';
|
||||
|
||||
ok !$act->confirm($workflow, 'not-the-token'), 'bad token';
|
||||
is $scratch{$st}, 'waiting', 'wait after bad';
|
||||
ok $act->confirm($workflow, 'test-token'), 'good token';
|
||||
is $scratch{$st}, 'done', 'done after good';
|
||||
60
www/extras/css/wg-datatable-html.css
Normal file
60
www/extras/css/wg-datatable-html.css
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* wg-datatable-html.css
|
||||
* CSS rules for DataTable assets
|
||||
*
|
||||
* Add as attachment to Default DetaTable template (HTML)
|
||||
*/
|
||||
|
||||
|
||||
/* start content at top of cells */
|
||||
.dataTable table>tbody>tr>td {
|
||||
vertical-align: 0;
|
||||
}
|
||||
.dataTable>table>tbody>tr>td>p:first-child { /* for htmlarea, but really applies to the starting p of any cell */
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* padding in all data cells */
|
||||
.dataTable table>tbody>tr>td {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* examples of further styles */
|
||||
|
||||
/* limited textarea/htmlarea cells, show vertical scrollbar if needed */
|
||||
/*
|
||||
.dataTable table>tbody>tr>td {
|
||||
height: 70px;
|
||||
width: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
*/
|
||||
|
||||
/* styling a particular DataTable in the page, by assetId */
|
||||
/*
|
||||
#dataTablesrvp2vk8QJqY5E0imSRQag.dataTable table>tbody>tr>td {
|
||||
color:red;
|
||||
height:auto;
|
||||
width:150px;
|
||||
overflow-y: visible
|
||||
}
|
||||
*/
|
||||
|
||||
/* minimum row height */
|
||||
/*
|
||||
.dataTable table>tbody>tr>td {
|
||||
min-height:70px;
|
||||
height:auto !important;
|
||||
height:70px;
|
||||
}
|
||||
*/
|
||||
|
||||
/* styling a particular column (e.g column 2), by assetId */
|
||||
/*
|
||||
#dataTablesvE67R6JQfmEI__T9pIAkQ.dataTable table>tbody>tr>td:first-child+td {
|
||||
color:blue;
|
||||
}
|
||||
*/
|
||||
|
||||
59
www/extras/css/wg-datatable-yui.css
Normal file
59
www/extras/css/wg-datatable-yui.css
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* wg-datatable-yui.css
|
||||
* CSS rules for DataTable assets
|
||||
*
|
||||
* Add as attachment to Default DetaTable template (YUI)
|
||||
*/
|
||||
|
||||
|
||||
/* start content at top of cells */
|
||||
.yui-dt-editable {
|
||||
vertical-align: 0;
|
||||
}
|
||||
.wg-dt-htmlarea>p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* padding in all data cells */
|
||||
td.yui-dt-editable .yui-dt-liner {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* examples of further styles */
|
||||
|
||||
/* limited textarea/htmlarea cells, show vertical scrollbar if needed */
|
||||
/*
|
||||
.wg-dt-textarea, .wg-dt-htmlarea {
|
||||
height: 70px;
|
||||
width: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* styling a particular DataTable in the page */
|
||||
/*
|
||||
#dataTablesrvp2vk8QJqY5E0imSRQag .wg-dt-textarea, #dataTablesrvp2vk8QJqY5E0imSRQag .wg-dt-htmlarea {
|
||||
color:red;
|
||||
height:auto;
|
||||
width:150px;
|
||||
overflow:visible;
|
||||
}
|
||||
*/
|
||||
|
||||
/* minimum row height */
|
||||
/*
|
||||
td.yui-dt-editable .yui-dt-liner {
|
||||
min-height:70px;
|
||||
height:auto !important;
|
||||
height:70px;
|
||||
}
|
||||
*/
|
||||
|
||||
/* styling a particular column */
|
||||
/*
|
||||
#dataTablesrvp2vk8QJqY5E0imSRQag .yui-dt-col-col1 .yui-dt-liner {
|
||||
color:blue;
|
||||
}
|
||||
*/
|
||||
21
www/extras/yui-webgui/build/form/datatable.css
Normal file
21
www/extras/yui-webgui/build/form/datatable.css
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* datatable.css
|
||||
* CSS rules for Edit DataTable
|
||||
*/
|
||||
|
||||
/* prevent collapsing of empty rows */
|
||||
.yui-dt-editable .yui-dt-liner {
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
/* displaying the initial blank line in HTMLarea is not useful */
|
||||
.wg-dt-htmlarea>p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* size of Textarea/HTMLarea cell */
|
||||
.wg-dt-textarea, .wg-dt-htmlarea {
|
||||
height: 50px;
|
||||
width: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
178
www/extras/yui-webgui/build/form/datatable.js
vendored
178
www/extras/yui-webgui/build/form/datatable.js
vendored
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*global WebGUI*/
|
||||
// Initialize namespace
|
||||
if (typeof WebGUI == "undefined") {
|
||||
|
|
@ -8,7 +7,6 @@ if (typeof WebGUI.Form == "undefined") {
|
|||
WebGUI.Form = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This object contains scripts for the DataTable form control
|
||||
*/
|
||||
|
|
@ -39,6 +37,21 @@ WebGUI.Form.DataTable
|
|||
this.options = options;
|
||||
this.schemaDialog = undefined;
|
||||
|
||||
/************************************************************************
|
||||
* editorByFormat ( event, data )
|
||||
* Return the DataTable editor type that matches the given format
|
||||
*/
|
||||
this.editorByFormat
|
||||
= function ( format ) {
|
||||
switch( format ) {
|
||||
case "text":
|
||||
case "number":
|
||||
case "link":
|
||||
return "textbox";
|
||||
}
|
||||
return format;
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* addRow ( event, data )
|
||||
* Add a row to the bottom of the table
|
||||
|
|
@ -59,11 +72,18 @@ WebGUI.Form.DataTable
|
|||
/************************************************************************
|
||||
* deleteSelectedRows ( )
|
||||
* Delete the selected rows after confirming
|
||||
* If there is an editor in the deleted row, cancel it
|
||||
*/
|
||||
this.deleteSelectedRows
|
||||
= function ( ) {
|
||||
if ( confirm( this.i18n.get( "Form_DataTable", "delete confirm" ) ) ) {
|
||||
var rows = this.dataTable.getSelectedRows();
|
||||
|
||||
// Cancel editor if present
|
||||
if ( this.dataTable.getCellEditor() ) {
|
||||
this.dataTable.cancelCellEditor();
|
||||
}
|
||||
|
||||
for ( var i = 0; i < rows.length; i++ ) {
|
||||
this.dataTable.deleteRow( this.dataTable.getRecord( rows[i] ) );
|
||||
}
|
||||
|
|
@ -98,16 +118,21 @@ WebGUI.Form.DataTable
|
|||
/************************************************************************
|
||||
* handleEditorKeyEvent ( obj )
|
||||
* Handle a keypress when the Cell Editor is open
|
||||
* Enter will close the editor and move down
|
||||
* Not implemented: Enter will close the editor and move down
|
||||
* Tab will close the editor and move right.
|
||||
* Use the handleTableKeyEvent() to handle the moving
|
||||
* Open a new cell editor on the newly focused cell
|
||||
*/
|
||||
this.handleEditorKeyEvent
|
||||
= function ( obj ) {
|
||||
// 9 = tab, 13 = enter
|
||||
var e = obj.event;
|
||||
if ( e.keyCode == 9 || e.keyCode == 13 ) {
|
||||
|
||||
// Avoid terminating the editor on enter
|
||||
if ( e.keyCode == 13) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( e.keyCode == 9) {
|
||||
var cell = this.dataTable.getCellEditor().getTdEl();
|
||||
var nextCell = this.dataTable.getNextTdEl( cell );
|
||||
this.dataTable.saveCellEditor();
|
||||
|
|
@ -121,8 +146,6 @@ WebGUI.Form.DataTable
|
|||
// No next cell, make a new row and open the editor for that one
|
||||
this.dataTable.addRow( {} );
|
||||
}
|
||||
// BUG: If pressing Enter, editor gets hidden right away due to YUI default event
|
||||
// putting e.preventDefault() and return false here makes no difference
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -152,7 +175,10 @@ WebGUI.Form.DataTable
|
|||
* handleTableKeyEvent ( obj )
|
||||
* Handle a keypress inside the DataTable
|
||||
* Space will open the cell editor
|
||||
* Note: it doesn't currently work: getSelectedTdEls() always returns [] when selectionMode is "standard"
|
||||
* Commented out for now.
|
||||
*/
|
||||
/*
|
||||
this.handleTableKeyEvent
|
||||
= function ( obj ) {
|
||||
// 9 = tab, 13 = enter, 32 = space
|
||||
|
|
@ -164,6 +190,7 @@ WebGUI.Form.DataTable
|
|||
}
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/************************************************************************
|
||||
* hideSchemaDialog ( )
|
||||
|
|
@ -204,7 +231,13 @@ WebGUI.Form.DataTable
|
|||
}
|
||||
|
||||
var dataTableOptions = {
|
||||
dateOptions : { format : this.options.dateFormat }
|
||||
dateOptions : {
|
||||
format : this.options.dateFormat,
|
||||
MSG_LOADING : this.i18n.get( "WebGUI", "Loading..." ),
|
||||
MSG_ERROR : this.i18n.get( "Form_DataTable", "data error" ),
|
||||
MSG_SORTASC : this.i18n.get( "Form_DataTable", "sort ascending" ),
|
||||
MSG_SORTDESC : this.i18n.get( "Form_DataTable", "sort descending" )
|
||||
}
|
||||
};
|
||||
|
||||
if ( this.options.showEdit ) {
|
||||
|
|
@ -215,6 +248,40 @@ WebGUI.Form.DataTable
|
|||
dataTableOptions.initialRequest = "";
|
||||
}
|
||||
|
||||
for ( var i = 0; i < this.columns.length; i++ ) {
|
||||
this.columns[ i ].editor = this.editorByFormat( this.columns[ i ].formatter );
|
||||
}
|
||||
|
||||
var widget = YAHOO.widget,
|
||||
DT = widget.DataTable;
|
||||
|
||||
// Dynamically add HTMLarea field type
|
||||
// HTMLAreaCellEditor is like TextareaCellEditor, but with an additional property "htmlarea" which is true
|
||||
var HTMLAreaCellEditor = function(a) {
|
||||
widget.TextareaCellEditor.superclass.constructor.call(this, a);
|
||||
};
|
||||
YAHOO.lang.extend( HTMLAreaCellEditor, widget.TextareaCellEditor, {
|
||||
htmlarea : true
|
||||
} );
|
||||
// Extend the static arrays of editors and formatters
|
||||
DT.Editors[ "htmlarea" ] = HTMLAreaCellEditor;
|
||||
|
||||
// Define classes "wg-dt-textarea" and "wg-dt-htmlarea" that can be overided by a stylesheet
|
||||
// (e.g. in the extraHeadTags of the asset).
|
||||
var formatter = function ( type ) {
|
||||
var fmt = function( el, oRecord, oColumn, oData ) {
|
||||
var value = YAHOO.lang.isValue(oData) ? oData : "";
|
||||
el.innerHTML = "<div class='wg-dt-" + type + "'>" + value + "</div>";
|
||||
};
|
||||
return fmt;
|
||||
};
|
||||
DT.Formatter[ "textarea" ] = formatter( "textarea" );
|
||||
DT.Formatter[ "htmlarea" ] = formatter( "htmlarea" );
|
||||
|
||||
// XXX need to do it with YUI API
|
||||
widget.BaseCellEditor.prototype.LABEL_SAVE = this.i18n.get( "Form_DataTable", "save" );
|
||||
widget.BaseCellEditor.prototype.LABEL_CANCEL = this.i18n.get( "Form_DataTable", "cancel" );
|
||||
|
||||
this.dataTable = new YAHOO.widget.DataTable(
|
||||
this.containerId,
|
||||
this.columns,
|
||||
|
|
@ -223,12 +290,52 @@ WebGUI.Form.DataTable
|
|||
);
|
||||
|
||||
if ( this.options.showEdit ) {
|
||||
var tinymceEdit = "tinymce-edit";
|
||||
var saveThis = this;
|
||||
|
||||
this.dataTable.doBeforeShowCellEditor = function( oCellEditor ) {
|
||||
if ( !oCellEditor.htmlarea ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
oCellEditor.getInputValue = function() {
|
||||
return tinyMCE.activeEditor.getContent();
|
||||
};
|
||||
|
||||
oCellEditor.textarea.setAttribute( 'id', tinymceEdit );
|
||||
tinyMCE.execCommand( 'mceAddControl', false, tinymceEdit );
|
||||
setTimeout(function(){ tinyMCE.execCommand( 'mceFocus',false, tinymceEdit ); }, 0);
|
||||
|
||||
// watch hitting tab, which should save the current cell and open an editor on the next
|
||||
tinyMCE.activeEditor.onKeyDown.add(
|
||||
function( eh, t ) {
|
||||
return function(ed, e) { // ed unused
|
||||
eh.call( t, { event: e } );
|
||||
};
|
||||
}( saveThis.handleEditorKeyEvent, saveThis )
|
||||
);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Remove TinyMCE on save or cancel
|
||||
var mceRemoveControl = function ( oArgs ) {
|
||||
var oCellEditor = oArgs.editor;
|
||||
if ( oCellEditor.htmlarea ) {
|
||||
tinyMCE.execCommand( 'mceRemoveControl', false, tinymceEdit );
|
||||
oCellEditor.textarea.removeAttribute( 'id' );
|
||||
}
|
||||
};
|
||||
this.dataTable.subscribe( "editorSaveEvent", mceRemoveControl );
|
||||
this.dataTable.subscribe( "editorCancelEvent", mceRemoveControl );
|
||||
|
||||
// Add the class so our editors get the right skin
|
||||
YAHOO.util.Dom.addClass( document.body, "yui-skin-sam" );
|
||||
|
||||
this.dataTable.subscribe( "cellDblclickEvent", this.dataTable.onEventShowCellEditor );
|
||||
this.dataTable.subscribe( "rowClickEvent", this.dataTable.onEventSelectRow );
|
||||
this.dataTable.subscribe( "tableKeyEvent", this.handleTableKeyEvent, this, true );
|
||||
/* this.handleTableKeyEvent() is commented out, see there for the reason */
|
||||
/* this.dataTable.subscribe( "tableKeyEvent", this.handleTableKeyEvent, this, true ); */
|
||||
this.dataTable.subscribe( "editorKeydownEvent", this.handleEditorKeyEvent, this, true );
|
||||
this.dataTable.subscribe( "editorShowEvent", this.handleEditorShowEvent, this, true );
|
||||
this.dataTable.subscribe( "rowAddEvent", this.handleRowAdd, this, true );
|
||||
|
|
@ -281,7 +388,6 @@ WebGUI.Form.DataTable
|
|||
scope : this
|
||||
}
|
||||
} );
|
||||
|
||||
// This data table will be submitted async
|
||||
if ( this.options.ajaxSaveUrl ) {
|
||||
var save = new YAHOO.widget.Button( {
|
||||
|
|
@ -333,6 +439,8 @@ WebGUI.Form.DataTable
|
|||
"format link",
|
||||
"format number",
|
||||
"format date",
|
||||
"format textarea",
|
||||
"format htmlarea",
|
||||
"add column",
|
||||
"cancel",
|
||||
"ok",
|
||||
|
|
@ -342,7 +450,13 @@ WebGUI.Form.DataTable
|
|||
"help select row",
|
||||
"help add row",
|
||||
"help default sort",
|
||||
"help reorder column"
|
||||
"help reorder column",
|
||||
"data error",
|
||||
"sort ascending",
|
||||
"sort descending"
|
||||
],
|
||||
'WebGUI' : [
|
||||
"Loading..."
|
||||
]
|
||||
},
|
||||
onpreload : {
|
||||
|
|
@ -365,7 +479,7 @@ WebGUI.Form.DataTable
|
|||
var helpDialog = new YAHOO.widget.Panel( "helpWindow", {
|
||||
modal : false,
|
||||
draggable : true,
|
||||
zIndex : 1000
|
||||
zIndex : 10000
|
||||
} );
|
||||
helpDialog.setHeader( "DataTable Help" );
|
||||
helpDialog.setBody(
|
||||
|
|
@ -404,28 +518,16 @@ WebGUI.Form.DataTable
|
|||
};
|
||||
|
||||
var buttonLabel = this.i18n.get( "Form_DataTable", "delete column" );
|
||||
var availableFormats = [
|
||||
{
|
||||
"value" : "text",
|
||||
"label" : this.i18n.get( "Form_DataTable", "format text" )
|
||||
},
|
||||
{
|
||||
"value" : "number",
|
||||
"label" : this.i18n.get( "Form_DataTable", "format number" )
|
||||
},
|
||||
{
|
||||
"value" : "email",
|
||||
"label" : this.i18n.get( "Form_DataTable", "format email" )
|
||||
},
|
||||
{
|
||||
"value" : "link",
|
||||
"label" : this.i18n.get( "Form_DataTable", "format link" )
|
||||
},
|
||||
{
|
||||
"value" : "date",
|
||||
"label" : this.i18n.get( "Form_DataTable", "format date" )
|
||||
}
|
||||
];
|
||||
var availableFormats = [];
|
||||
var formatType = [ "text", "number", "email", "link", "date", "textarea", "htmlarea" ];
|
||||
for ( var fti = 0; fti < formatType.length; fti++) {
|
||||
availableFormats.push(
|
||||
{
|
||||
"value" : formatType[fti],
|
||||
"label" : this.i18n.get( "Form_DataTable", "format " + formatType[fti] )
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// function for creating new database columns to the table schema
|
||||
var createTableColumn = function(i,cols) {
|
||||
|
|
@ -595,7 +697,7 @@ WebGUI.Form.DataTable
|
|||
this.updateSchema
|
||||
= function () {
|
||||
var data = this.schemaDialog.getData();
|
||||
|
||||
|
||||
// First delete columns
|
||||
var deleteCols = YAHOO.lang.JSON.parse( data[ "deleteCols" ] );
|
||||
for ( var x = 0; x < deleteCols.length; x++ ) {
|
||||
|
|
@ -623,7 +725,7 @@ WebGUI.Form.DataTable
|
|||
var rows = this.dataTable.getRecordSet().getRecords();
|
||||
for ( var r = 0; r < rows.length; r++ ) {
|
||||
rows[ r ].setData( newKey, rows[ r ].getData( oldKey ) );
|
||||
rows[ r ].setData( oldKey, undefined );
|
||||
rows[ r ].setData( oldKey, undefined );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -633,7 +735,7 @@ WebGUI.Form.DataTable
|
|||
formatter : format,
|
||||
resizeable : ( col ? col.resizeable : 1 ),
|
||||
sortable : ( col ? col.sortable : 1 ),
|
||||
editor : ( format == "date" ? "date" : "textbox")
|
||||
editor : this.editorByFormat( format )
|
||||
};
|
||||
if ( format == "date" ) {
|
||||
newCol["dateOptions"] = { format : this.options.dateFormat };
|
||||
|
|
@ -664,9 +766,9 @@ WebGUI.Form.DataTable
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
this.dataTable.render();
|
||||
this.schemaDialog.cancel();
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue