merge to 10219

This commit is contained in:
Colin Kuskie 2009-04-08 16:35:31 +00:00
parent ae28bf79c8
commit 4c1307e3d0
194 changed files with 8203 additions and 2134 deletions

View file

@ -1,3 +1,44 @@
7.7.3
- fixed #10094: double explanation in thread help
- rfe #9612: Carousel Wobject (was Widget Wobject) (SDH Consulting Group)
- Survey summaries now added. In the Survey edit, select quiz mode, and a summary will be shown to the user at the end of the survey.
7.7.2
- fixed #10056: YUI javascripts included while adminOff (BNC)
- fixed a bug that required you to hit "update cart" before the checkout
button would appear
- fixed #9955: Matrix alpha sort is backwards
- fixed #9934: Matrix 2.0 - Products should be listed alphabetically
- fixed #9865: Matrix: Later added attributes are not detected in older listings
- added: Sku's now have a getAddToCartForm method, which is used by the Shelf
to place a small form next to each Sku that displays. Sku's with complex
forms (like the EMS sku's), simply display a button to take the user to the
sku's view screen for the whole form. [TEMPLATE]
- fixed #9933: Matrix 2.0 - Unable to view/edit product maintainer account
- fixed #9951: Matrix 2.0: Median not calculated correctly
- added new AssetAspect::RssFeed (Matthew Wilson) - to convert an asset to use it (see
Collaboration.pm as an example), inherit from Class::C3 as in Collaboration
and you'll need to remove all your ->SUPER::xxxxx invocations - usually replace it
with ->next::method, but when your SUPER was previously calling a method with
a name different from your current method, you'll need to specify the parent/super
module name explicity (e.g. ->WebGUI::Asset::Wobject::canEdit()). You'll also
need to implement the getRssFeedItems method as explained in AssetAspect/RssFeed.pm
- Survey now shows warnings if bad goto, bad gotoExpressions, no question text, survey looping,
or no question answers are found.
- fixed: Gateway problem with VendorPayout.
- fixed #9976: carts not cleaned up
- fixed Problems with displaying the wrong name and/or creation in the Account, and all plugins.
7.7.1
- the AdSku project: create a Sku that allows buyers to purchase advertising in select AdSpaces at selected priorities
- rfe #9353: Welcome message template (SDH Consulting Group)
- rfe #10007: New Month and Year question type. If required, Month must be selected and a 4 digit year must be entered.
- fixed #10011: Wrong spelling of "Descendant" in Navigation Asset Help function (Rob Schipper/NFIA India)
- rfe #9098: Thingy Thing-copy function (SDH Consulting Group)
- rfe #9099: Thingy field-copy function (SDH Consulting Group)
- Keywords are now comma separated rather than space separated.
- added Keywords form control with autocomplete
- Survey: Moved multiple choice questions to the data base, and they can now be edited globally from the Survey editor
7.7.0
- fixed #9913: New Content Side Bar missing in Asset window
- fixed: New Mail macro never returns any messages
@ -18,6 +59,19 @@
- fixed #9956: Product Import
- added Survey now has a loading mask on Survey edit ajax calls.
- fixed: Sliders fixed. Improved algorithm for determining pixel step size.
- rfe #9355: Password Recovery email subject (SDH Consulting Group)
- added: Users can now set a minimum cart amount required for checkout. ( Martin Kamerbeek / Oqapi )
- fixed validation issue in the donation asset ( Martin kamerbeek / Oqapi )
- rfe #9354: Account activation email template (SDH Consulting Group)
- rfe #9233: Survey branching expressions (Patrick Donelan, SDH Consulting Group)
- rfe #9202: Survey Jump-to combo box (Patrick Donelan, SDH Consulting Group)
- rfe #9201: Make Survey TextAreas YUI resizable (SDH Consulting Group)
- rfe #9200: Survey Text/TextArea handling (SDH Consulting Group)
- rfe #9199: Survey final page detection (SDH Consulting Group)
- rfe #9198: Turn Survey Edit Section/Question/Answer fields into RTEs (SDH Consulting Group)
- rfe #9197: Survey timeout handling (SDH Consulting Group)
- rfe #8862: Passive Analytics (SDH Consulting Group)
- fixed #9979: i18n labels for Gallery (Knowmad Technologies)
7.6.14
- fixed: IE6 shows Admin Bar over Asset Manager

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,25 @@ upgrading from one version to the next, or even between multiple
versions. Be sure to heed the warnings contained herein as they will
save you many hours of grief.
7.7.2
--------------------------------------------------------------------
* WebGUI now requires Clone version 0.31 or greater.
* You must upgrade to WebGUI 7.7.2 before going on to higher versions of WebGUI due
to changes in the database table for the Cart.
* The RSSCapable class and RSSFromParent asset have been removed.
Any custom assets using those are recommended to switch to the new
RssFeed AssetAspect. If not used by custom assets, the tables
used by them will be removed from the database.
7.7.1
--------------------------------------------------------------------
* Keywords are now comma separated rather than space separated. Quotes are
no longer treated specially and will become part of keywords. Keywords
cannot contain commas. Keyword searching is still case insensitive, but
keyword will preserve the case of what is entered.
7.7.0
--------------------------------------------------------------------
* WebGUI now requires Params::Validate version 0.81 or greater.

File diff suppressed because one or more lines are too long

View file

@ -60,21 +60,19 @@ sub addPackage {
# Import the package into the import node
my $package = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
# Make the package not a package anymore
$package->update({ isPackage => 0 });
# Set the default flag for templates added
my $assetIds
= $package->getLineage( ['self','descendants'], {
includeOnlyClasses => [ 'WebGUI::Asset::Template' ],
} );
# Turn off the package flag, and set the default flag for templates added
my $assetIds = $package->getLineage( ['self','descendants'] );
for my $assetId ( @{ $assetIds } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
if ( !$asset ) {
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
next;
}
$asset->update( { isDefault => 1 } );
my $properties = { isPackage => 0 };
if ($asset->isa('WebGUI::Asset::Template')) {
$properties->{isDefault} = 1;
}
$asset->update( $properties );
}
return;

Binary file not shown.

Binary file not shown.

View file

@ -32,17 +32,14 @@ my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
addAccountActivationTemplateToSettings( $session );
addGroupToAddToMatrix( $session );
addScreenshotTemplatesToMatrix( $session );
surveyDoAfterTimeLimit($session);
surveyRemoveResponseTemplate($session);
surveyEndWorkflow($session);
installAssetHistory($session);
# Story Manager
installStoryManagerTables($session);
sm_upgradeConfigFiles($session);
sm_updateDailyWorkflow($session);
addMinimumCartCheckoutSetting( $session );
# Passive Analytics
pa_installLoggingTables($session);
@ -58,6 +55,16 @@ createShopAcccountPluginSettings( $session );
finish($session); # this line required
#----------------------------------------------------------------------------
sub addAccountActivationTemplateToSettings {
my $session = shift;
print "\tAdding account activation template to settings \n" unless $quiet;
$session->db->write("insert into settings (name, value) values ('webguiAccountActivationTemplate','PBtmpl0000000000000016')");
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addGroupToAddToMatrix {
my $session = shift;
@ -115,7 +122,7 @@ sub installAssetHistory {
print "\tAdding Asset History content handler... \n" unless $quiet;
##Content Handler
my $contentHandlers = $session->config->get('contentHandlers');
if (! isIn('WebGUI::Content::Handler', @{ $contentHandlers }) ) {
if (! isIn('WebGUI::Content::AssetHistory', @{ $contentHandlers }) ) {
my @newHandlers = ();
foreach my $handler (@{ $contentHandlers }) {
push @newHandlers, $handler;
@ -307,13 +314,23 @@ sub addTransactionItemFlags {
#----------------------------------------------------------------------------
sub createShopAcccountPluginSettings {
my $session = shift;
print "Creating default settings for the account plugin..." unless $quiet;
print "\tCreating default settings for the account plugin..." unless $quiet;
$session->setting->add('shopMySalesTemplateId', '-zxyB-O50W8YnL39Ouoc4Q');
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addMinimumCartCheckoutSetting {
my $session = shift;
print "\tAdding setting for minimum cart checkout..." unless $quiet;
$session->setting->add( 'shopCartCheckoutMinimum', '0.00' );
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
# Describe what our function does
#sub exampleFunction {
@ -323,97 +340,6 @@ sub createShopAcccountPluginSettings {
# print "DONE!\n" unless $quiet;
#}
sub installStoryManagerTables {
my ($session) = @_;
print "\tAdding Story Manager tables... " unless $quiet;
my $db = $session->db;
$db->write(<<EOSTORY);
CREATE TABLE Story (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
headline CHAR(255),
subtitle CHAR(255),
byline CHAR(255),
location CHAR(255),
highlights TEXT,
story MEDIUMTEXT,
photo LONGTEXT,
PRIMARY KEY ( assetId, revisionDate )
)
EOSTORY
$db->write(<<EOARCHIVE);
CREATE TABLE StoryArchive (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPerFeed INTEGER,
storiesPerPage INTEGER,
groupToPost CHAR(22) BINARY,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
editStoryTemplateId CHAR(22) BINARY,
archiveAfter INT(11),
richEditorId CHAR(22) BINARY,
approvalWorkflowId CHAR(22) BINARY DEFAULT 'pbworkflow000000000003',
PRIMARY KEY ( assetId, revisionDate )
)
EOARCHIVE
$db->write(<<EOTOPIC);
CREATE TABLE StoryTopic (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPer INTEGER,
storiesShort INTEGER,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
PRIMARY KEY ( assetId, revisionDate )
)
EOTOPIC
print "DONE!\n" unless $quiet;
}
sub sm_upgradeConfigFiles {
my ($session) = @_;
print "\tAdding Story Manager to config file... " unless $quiet;
my $config = $session->config;
$config->addToHash(
'assets',
'WebGUI::Asset::Wobject::StoryTopic' => {
'category' => 'community'
},
);
$config->addToHash(
'assets',
"WebGUI::Asset::Wobject::StoryArchive" => {
"isContainer" => 1,
"category" => "community"
},
);
my $activities = $config->get('workflowActivities');
my $none = $activities->{None};
if (!isIn('WebGUI::Workflow::Activity::ArchiveOldStories', @{ $none })) {
unshift @{ $none }, 'WebGUI::Workflow::Activity::ArchiveOldStories';
}
$config->set('workflowActivities', $activities);
print "DONE!\n" unless $quiet;
}
sub sm_updateDailyWorkflow {
my ($session) = @_;
print "\tAdding Archive Old Stories to Daily Workflow... " unless $quiet;
my $workflow = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
foreach my $activity (@{ $workflow->getActivities }) {
return if $activity->getName() eq 'WebGUI::Workflow::Activity::ArchiveOldStories';
}
my $activity = $workflow->addActivity('WebGUI::Workflow::Activity::ArchiveOldStories');
$activity->set('title', 'Archive Old Stories');
$activity->set('description', 'Archive old stories, based on the settings of the Story Archives that own them');
print "DONE!\n" unless $quiet;
}
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------

View file

@ -0,0 +1,237 @@
#!/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.7.1';
my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
adSkuInstall($session);
addWelcomeMessageTemplateToSettings( $session );
addStatisticsCacheTimeoutToMatrix( $session );
removeOldSettings( $session );
#add Survey table
addSurveyQuestionTypes($session);
# image mods
addImageAnnotation($session);
# rss mods
addRssLimit($session);
finish($session); # this line required
# remove old settings that aren't used any more
sub removeOldSettings {
my $session = shift;
print "\tRemoving old, unused settings... " unless $quiet;
my $setting = $session->setting;
$setting->remove('commerceCheckoutCanceledTemplateId');
$setting->remove('commerceConfirmCheckoutTemplateId');
$setting->remove('commerceEnableSalesTax');
$setting->remove('commercePaymentPlugin');
$setting->remove('commercePurchaseHistoryTemplateId');
$setting->remove('commerceSelectPaymentGatewayTemplateId');
$setting->remove('commerceSelectShippingMethodTemplateId');
$setting->remove('commerceSendDailyReportTo');
$setting->remove('commerceViewShoppingCartTemplateId');
print "Done.\n" unless $quiet;
}
sub addSurveyQuestionTypes{
my $session = shift;
print "\tAdding new survey table Survey_questionTypes... " unless $quiet;
$session->db->write("
CREATE TABLE `Survey_questionTypes` (
`questionType` varchar(56) NOT NULL,
`answers` text NOT NULL,
PRIMARY KEY (`questionType`))
");
$session->db->write(q{
INSERT INTO `Survey_questionTypes` VALUES ('Scale',''),('Gender','Male,Female'),('Education','Elementary or some high school,High school/GED,Some college/vocational school,College graduate,Some graduate work,Master\\'s degree,Doctorate (of any type),Other degree (verbatim)'),('Importance','Not at all important,,,,,,,,,,Extremely important'),('Yes/No','Yes,No'),('Confidence','Not at all confident,,,,,,,,,,Extremely confident'),('Effectiveness','Not at all effective,,,,,,,,,,Extremely effective'),('Oppose/Support','Strongly oppose,,,,,,Strongly support'),('Certainty','Not at all certain,,,,,,,,,,Extremely certain'),('True/False','True,False'),('Concern','Not at all concerned,,,,,,,,,,Extremely concerned'),('Ideology','Strongly liberal,Liberal,Somewhat liberal,Middle of the road,Slightly conservative,Conservative,Strongly conservative'),('Security','Not at all secure,,,,,,,,,,Extremely secure'),('Risk','No risk,,,,,,,,,,Extreme risk'),('Agree/Disagree','Strongly disagree,,,,,,Strongly agree'),('Race','American Indian,Asian,Black,Hispanic,White non-Hispanic,Something else (verbatim)'),('Threat','No threat,,,,,,,,,,Extreme threat'),('Party','Democratic party,Republican party (or GOP),Independent party,Other party (verbatim)'),('Likelihood','Not at all likely,,,,,,,,,,Extremely likely'),('Multiple Choice',''),('Satisfaction','Not at all satisfied,,,,,,,,,,Extremely satisfied')
});
print "Done.\n" unless $quiet;
}
sub addWelcomeMessageTemplateToSettings {
my $session = shift;
print "\tAdding welcome message template to settings... " unless $quiet;
$session->db->write("insert into settings values ('webguiWelcomeMessageTemplate', 'PBtmpl0000000000000015');");
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addRssLimit {
my $session = shift;
print "\tAdding rssLimit to RSSCapable table, if needed... " unless $quiet;
my $sth = $session->db->read('describe RSSCapable rssCapableRssLimit');
if (! defined $sth->hashRef) {
$session->db->write("alter table RSSCapable add column rssCapableRssLimit integer");
}
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addImageAnnotation {
my $session = shift;
print "\tAdding annotations to ImageAsset table, if needed... " unless $quiet;
my $sth = $session->db->read('describe ImageAsset annotations');
if (! defined $sth->hashRef) {
$session->db->write("alter table ImageAsset add column annotations mediumtext");
}
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addStatisticsCacheTimeoutToMatrix{
my $session = shift;
print "\tAdding statisticsCacheTimeout setting to Matrix table... " unless $quiet;
$session->db->write("alter table Matrix add statisticsCacheTimeout int(11) not null default 3600");
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
# Describe what our function does
sub adSkuInstall {
my $session = shift;
print "\tInstalling the AdSku Asset...\n" unless $quiet;
print "\t\tCreate AdSku database table.\n" unless $quiet;
$session->db->write("CREATE TABLE AdSku (
assetId VARCHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
purchaseTemplate VARCHAR(22) BINARY NOT NULL,
manageTemplate VARCHAR(22) BINARY NOT NULL,
adSpace VARCHAR(22) BINARY NOT NULL,
priority INTEGER DEFAULT '1',
pricePerClick Float DEFAULT '0',
pricePerImpression Float DEFAULT '0',
clickDiscounts VARCHAR(1024) default '',
impressionDiscounts VARCHAR(1024) default '',
PRIMARY KEY (assetId,revisionDate)
)");
print "\t\tCreate Adsku crud table.\n" unless $quiet;
use WebGUI::AssetCollateral::Sku::Ad::Ad;
WebGUI::AssetCollateral::Sku::Ad::Ad->crud_createTable($session);
print "\t\tAdding to config file.\n" unless $quiet;
$session->config->addToHash("assets", 'WebGUI::Asset::Sku::Ad' => { category => 'shop' } );
print "\tDone.\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;
# 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 = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
# Make the package not a package anymore
$package->update({ isPackage => 0 });
# Set the default flag for templates added
my $assetIds
= $package->getLineage( ['self','descendants'], {
includeOnlyClasses => [ 'WebGUI::Asset::Template' ],
} );
for my $assetId ( @{ $assetIds } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
if ( !$asset ) {
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
next;
}
$asset->update( { isDefault => 1 } );
}
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',".$session->datetime->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

@ -0,0 +1,284 @@
#!/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;
use WebGUI::Workflow;
use WebGUI::Utility;
my $toVersion = '7.7.2';
my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
recalculateMatrixListingMedianValue( $session );
addRssFeedAspect($session);
addRssFeedAspectToAssets($session);
convertCollaborationToRssAspect($session);
removeRssCapableAsset($session);
addCreationTimeToCart($session);
addCartKillerActivityToConfig($session);
addCartKillerActivityToWorkflow($session);
finish($session); # this line required
#----------------------------------------------------------------------------
sub recalculateMatrixListingMedianValue{
my $session = shift;
print "\tRecalculating median value for Matrix Listing ratings... " unless $quiet;
my $matrices = WebGUI::Asset->getRoot($session)->getLineage(['descendants'],
{
statesToInclude => ['published','trash','clipboard','clipboard-limbo','trash-limbo'],
statusToInclude => ['pending','approved','deleted','archived'],
includeOnlyClasses => ['WebGUI::Asset::Wobject::Matrix'],
returnObjects => 1,
});
for my $matrix (@{$matrices})
{
next unless defined $matrix;
my %categories = keys %{$matrix->getCategories};
my $listings = $session->db->read("select distinct listingId from MatrixListing_rating where assetId = ?"
,[$matrix->getId]);
while (my $listing= $listings->hashRef){
foreach my $category (%categories) {
my $half = $session->db->quickScalar("select round((select count(*) from MatrixListing_rating where
listingId = ? and category = ?)/2)",[$listing->{listingId},$category]);
my $medianValue = $session->db->quickScalar("select rating from MatrixListing_rating where listingId =?
and category =? order by rating limit $half,1;",[$listing->{listingId},$category]);
$session->db->write("update MatrixListing_ratingSummary set medianValue = ? where listingId = ? and
category = ?",[$medianValue,$listing->{listingId},$category]);
}
}
}
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addRssFeedAspect {
my $session = shift;
print "\tAdding RssFeed asset aspect..." unless $quiet;
$session->db->write(q{create table assetAspectRssFeed (
assetId char(22) binary not null,
revisionDate bigint not null,
itemsPerFeed int(11) default 25,
feedCopyright text,
feedTitle text,
feedDescription mediumtext,
feedImage char(22) binary,
feedImageLink text,
feedImageDescription mediumtext,
feedHeaderLinks char(32) default 'rss\natom',
primary key (assetId, revisionDate)
)});
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addRssFeedAspectToAssets {
my $session = shift;
my $db = $session->db;
foreach my $asset_class (qw( WikiMaster SyndicatedContent Gallery GalleryAlbum )) {
print "\tAdding RssFeed aspect to $asset_class table..." unless $quiet;
my $pages = $db->read("select assetId,revisionDate from $asset_class");
while (my ($id, $rev) = $pages->array) {
$db->write("INSERT INTO assetAspectRssFeed (assetId, revisionDate, itemsPerFeed, feedTitle, feedDescription, feedImage, feedImageLink, feedImageDescription) VALUES (?,?,25,'','',NULL,'','')",[$id,$rev]);
}
print "Done.\n" unless $quiet;
}
}
#----------------------------------------------------------------------------
sub convertCollaborationToRssAspect {
my $session = shift;
print "\tAdding RssFeed aspect to Collaboration, (porting rssCapableRssLimit to itemsPerFeed)..." unless $quiet;
my $db = $session->db;
my @rssFromParents;
my $pages = $db->read("SELECT Collaboration.assetId, Collaboration.revisionDate, RSSCapable.rssCapableRssLimit, RSSCapable.rssCapableRssFromParentId, RSSCapable.rssCapableRssEnabled FROM Collaboration INNER JOIN RSSCapable ON Collaboration.assetId=RSSCapable.assetId AND Collaboration.revisionDate=RSSCapable.revisionDate");
while (my ($id, $rev, $limit, $fromParent, $enabled) = $pages->array) {
if ($fromParent) {
push @rssFromParents, $fromParent;
}
my $headerLinks = $enabled ? "rss\natom" : q{};
$db->write("INSERT INTO assetAspectRssFeed (assetId, revisionDate, itemsPerFeed, feedTitle, feedDescription, feedImage, feedImageLink, feedImageDescription, feedHeaderLinks) VALUES (?,?,?,'','',NULL,'','',?)",[$id,$rev,$limit || 25, $headerLinks]);
}
for my $assetId (@rssFromParents) {
my $asset = eval { WebGUI::Asset->newPending($session, $assetId) };
if ($asset) {
$asset->purge;
}
}
$db->write("DELETE FROM RSSCapable WHERE assetId IN (SELECT assetId FROM Collaboration GROUP BY assetId)");
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub removeRssCapableAsset {
my $session = shift;
print "\tChecking for uses of RSSCapable...\n" unless $quiet;
my @rssCapableClasses = $session->db->buildArray('SELECT className FROM RSSCapable INNER JOIN asset ON RSSCapable.assetId=asset.assetId GROUP BY className');
if (@rssCapableClasses) {
warn "\t\tThis site is using the assets\n\t\t\t" . join(', ', @rssCapableClasses) . "\n\t\twhich use the RSSCapable class! Support RSSCapable has been dropped and it will no longer be maintained.\n";
}
else {
print "\t\tNot used, removing.\n" unless $quiet;
$session->db->write("DROP TABLE RSSCapable");
$session->db->write("DROP TABLE RSSFromParent");
my $rssCapableTemplates = WebGUI::Asset->getRoot($session)->getLineage(['descendants'], {
statesToInclude => [qw(published clipboard clipboard-limbo trash-limbo)],
statusToInclude => [qw(approved pending archived)],
returnObjects => 1,
includeOnlyClasses => ['WebGUI::Asset::Template'],
joinClass => 'WebGUI::Asset::Template',
whereClause => q{template.namespace = 'RSSCapable/RSS'},
});
for my $template (@{$rssCapableTemplates}) {
$template->trash;
}
}
print "\tDone.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addCreationTimeToCart {
my $session = shift;
print "\tAdding creation time to cart..." unless $quiet;
$session->db->write("alter table cart add column creationDate int(20)");
$session->db->write('update cart set creationDate=NOW()');
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addCartKillerActivityToConfig {
my $session = shift;
print "\tAdding Remove Old Carts workflow activity to config files..." unless $quiet;
my $activities = $session->config->get('workflowActivities');
my $none = $activities->{'None'};
if (!isIn('WebGUI::Workflow::Activity::RemoveOldCarts', @{ $none })) {
push @{ $none }, 'WebGUI::Workflow::Activity::RemoveOldCarts';
}
$session->config->set('workflowActivities', $activities);
print "Done.\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addCartKillerActivityToWorkflow {
my $session = shift;
print "\tAdding Remove Old Carts workflow activity to Daily Workflow..." unless $quiet;
my $workflow = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
my $removeCarts = $workflow->addActivity('WebGUI::Workflow::Activity::RemoveOldCarts');
$removeCarts->set('title', 'Remove old carts');
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;
# 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 = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
# Make the package not a package anymore
$package->update({ isPackage => 0 });
# Set the default flag for templates added
my $assetIds
= $package->getLineage( ['self','descendants'], {
includeOnlyClasses => [ 'WebGUI::Asset::Template' ],
} );
for my $assetId ( @{ $assetIds } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
if ( !$asset ) {
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
next;
}
$asset->update( { isDefault => 1 } );
}
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',".$session->datetime->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

@ -0,0 +1,240 @@
#!/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;
use WebGUI::Utility;
my $toVersion = '7.7.3';
my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
addSurveyQuizModeColumns($session);
addSurveyExpressionEngineConfigFlag($session);
# Story Manager
installStoryManagerTables($session);
sm_upgradeConfigFiles($session);
sm_updateDailyWorkflow($session);
finish($session); # this line required
#----------------------------------------------------------------------------
# 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;
#}
sub addSurveyQuizModeColumns{
my $session = shift;
print "\tAdding columns to Survey table... " unless $quiet;
$session->db->write("alter table Survey add column `quizModeSummary` TINYINT(3)");
$session->db->write("alter table Survey add column `surveySummaryTemplateId` char(22)");
print "Done.\n" unless $quiet;
}
sub addSurveyExpressionEngineConfigFlag{
my $session = shift;
print "\tAdding enableSurveyExpressionEngine config option... " unless $quiet;
$session->config->set('enableSurveyExpressionEngine', 0);
print "Done.\n" unless $quiet;
}
sub installStoryManagerTables {
my ($session) = @_;
print "\tAdding Story Manager tables... " unless $quiet;
my $db = $session->db;
$db->write(<<EOSTORY);
CREATE TABLE Story (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
headline CHAR(255),
subtitle CHAR(255),
byline CHAR(255),
location CHAR(255),
highlights TEXT,
story MEDIUMTEXT,
photo LONGTEXT,
PRIMARY KEY ( assetId, revisionDate )
)
EOSTORY
$db->write(<<EOARCHIVE);
CREATE TABLE StoryArchive (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPerFeed INTEGER,
storiesPerPage INTEGER,
groupToPost CHAR(22) BINARY,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
editStoryTemplateId CHAR(22) BINARY,
archiveAfter INT(11),
richEditorId CHAR(22) BINARY,
approvalWorkflowId CHAR(22) BINARY DEFAULT 'pbworkflow000000000003',
PRIMARY KEY ( assetId, revisionDate )
)
EOARCHIVE
$db->write(<<EOTOPIC);
CREATE TABLE StoryTopic (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPer INTEGER,
storiesShort INTEGER,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
PRIMARY KEY ( assetId, revisionDate )
)
EOTOPIC
print "DONE!\n" unless $quiet;
}
sub sm_upgradeConfigFiles {
my ($session) = @_;
print "\tAdding Story Manager to config file... " unless $quiet;
my $config = $session->config;
$config->addToHash(
'assets',
'WebGUI::Asset::Wobject::StoryTopic' => {
'category' => 'community'
},
);
$config->addToHash(
'assets',
"WebGUI::Asset::Wobject::StoryArchive" => {
"isContainer" => 1,
"category" => "community"
},
);
my $activities = $config->get('workflowActivities');
my $none = $activities->{None};
if (!isIn('WebGUI::Workflow::Activity::ArchiveOldStories', @{ $none })) {
unshift @{ $none }, 'WebGUI::Workflow::Activity::ArchiveOldStories';
}
$config->set('workflowActivities', $activities);
print "DONE!\n" unless $quiet;
}
sub sm_updateDailyWorkflow {
my ($session) = @_;
print "\tAdding Archive Old Stories to Daily Workflow... " unless $quiet;
my $workflow = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
foreach my $activity (@{ $workflow->getActivities }) {
return if $activity->getName() eq 'WebGUI::Workflow::Activity::ArchiveOldStories';
}
my $activity = $workflow->addActivity('WebGUI::Workflow::Activity::ArchiveOldStories');
$activity->set('title', 'Archive Old Stories');
$activity->set('description', 'Archive old stories, based on the settings of the Story Archives that own them');
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;
# 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 = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
# Make the package not a package anymore
$package->update({ isPackage => 0 });
# Set the default flag for templates added
my $assetIds
= $package->getLineage( ['self','descendants'], {
includeOnlyClasses => [ 'WebGUI::Asset::Template' ],
} );
for my $assetId ( @{ $assetIds } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
if ( !$asset ) {
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
next;
}
$asset->update( { isDefault => 1 } );
}
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',".$session->datetime->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

@ -493,6 +493,9 @@
"WebGUI::Asset::Wobject::DataForm" : {
"category" : "basic"
},
"WebGUI::Asset::Sku::Ad" : {
"category" : "shop"
},
"WebGUI::Asset::Sku::Donation" : {
"category" : "shop"
},
@ -819,6 +822,7 @@
"WebGUI::Workflow::Activity::NotifyAdminsWithOpenVersionTags",
"WebGUI::Workflow::Activity::PurgeOldAssetRevisions",
"WebGUI::Workflow::Activity::PurgeOldTrash",
"WebGUI::Workflow::Activity::RemoveOldCarts",
"WebGUI::Workflow::Activity::SendQueuedMailMessages",
"WebGUI::Workflow::Activity::SummarizePassiveProfileLog",
"WebGUI::Workflow::Activity::SyncProfilesToLdap",

View file

@ -1,7 +1,7 @@
package WebGUI;
our $VERSION = '7.7.0';
our $VERSION = '7.7.3';
our $STATUS = 'beta';

View file

@ -51,7 +51,7 @@ sub appendCommonVars {
my $self = shift;
my $var = shift;
my $session = $self->session;
my $user = $session->user;
my $user = $self->getUser;
$var->{'user_full_name' } = $user->getWholeName;
$var->{'user_member_since'} = $user->dateCreated;
@ -242,7 +242,9 @@ sub editSettingsFormSave {
=head2 getLayoutTemplateId ( )
Override this method to return the template Id for the account layout.
Override this method to return the template Id for the account layout. The default
account layout draws a tabbed interface to the different account plugins, and displays
the content from a particular screen from the account plugin.
=cut
@ -253,9 +255,10 @@ sub getLayoutTemplateId {
#-------------------------------------------------------------------
=head2 getStyleTemplate ( )
=head2 getStyleTemplateId ( )
Override this method to return the template for the main style.
Override this method to return the template for the main style. The style would
be for the page that the account layout template is embedded in.
=cut
@ -278,8 +281,8 @@ the current module and do values will be used.
=head3 appendUID
If this flag is set and uid is passed along the url, the uid passed in will be
appended to the end of it to the end of the url
If this flag is set and uid is passed as a URL param, that uid will be
appended to the end of the url.
=cut
@ -289,8 +292,8 @@ sub getUrl {
my $appendUID = shift;
my $session = $self->session;
my $form = $session->form;
my $uid = $self->uid;
if($pairs) {
#Append op=account to the url if it doesn't already exist
unless ($pairs =~ m/op=account/){
@ -301,7 +304,6 @@ sub getUrl {
$pairs = q{op=account;module=}.$self->module.q{;do=}.$self->method;
}
my $uid = $self->uid;
$pairs .= ";uid=".$uid if($appendUID && $uid);
return $session->url->page($pairs);
@ -309,6 +311,25 @@ sub getUrl {
#-------------------------------------------------------------------
=head2 getUser
Gets the user, either specified by the uid URL parameter, or the
session user.
=cut
sub getUser {
my $self = shift;
if ($self->uid) {
return WebGUI::User->new($self->session, $self->uid);
}
else {
return $self->session->user;
}
}
#-------------------------------------------------------------------
=head2 new ( session, module [,method ,uid] )
Constructor.

View file

@ -226,12 +226,6 @@ sub www_view {
$self->appendCommonVars($var);
$p->appendTemplateVars($var);
#Overwrite these
my $user = WebGUI::User->new($session,$userId);
$var->{'user_full_name' } = $user->getWholeName;
$var->{'user_member_since' } = $user->dateCreated;
return $self->processTemplate($var,$self->getViewTemplateId);
}

View file

@ -232,7 +232,7 @@ sub getLayoutTemplateId {
#-------------------------------------------------------------------
=head2 getConfirmTemplateId ( )
=head2 getRemoveConfirmTemplateId ( )
This method returns the template ID for the confirmation screen.
@ -484,17 +484,13 @@ sub www_view {
my $var = {};
my $uid = $self->uid;
my $user = ($uid) ? WebGUI::User->new($session,$uid) : $session->user;
my $user = $self->getUser;
$self->appendCommonVars($var);
my $displayView = $uid ne "";
$var->{'display_message'} = $msg;
#Override these
$var->{'user_full_name' } = $user->getWholeName;
$var->{'user_member_since' } = $user->dateCreated;
unless ($user->profileField('ableToBeFriend') && $user->profileIsViewable($session->user)) {
my $i18n = WebGUI::International->new($session,"Account_Friends");
my $errorMsg = "";

View file

@ -107,13 +107,13 @@ sub appendCommonVars {
my $self = shift;
my $var = shift;
my $session = $self->session;
my $user = $session->user;
my $user = $self->getUser;
my $pageUrl = $session->url->page;
$self->SUPER::appendCommonVars($var);
$var->{'edit_profile_url' } = $self->getUrl("module=profile;do=edit");
$var->{'invitations_enabled' } = $session->user->profileField('ableToBeFriend');
$var->{'invitations_enabled' } = $user->profileField('ableToBeFriend');
$var->{'profile_category_loop'} = [];
#Append the categories
@ -475,10 +475,6 @@ sub www_view {
$self->appendCommonVars($var);
#Overwrite these
$var->{'user_full_name' } = $user->getWholeName;
$var->{'user_member_since' } = $user->dateCreated;
$var->{'profile_user_id' } = $user->userId;
$var->{'can_edit_profile' } = $uid eq $session->user->userId;
#Check user privileges

View file

@ -31,7 +31,7 @@ These methods are available from this class:
=head2 canView ( )
Returns whether or not the user can view the the tab for this module
Returns whether or not the user can view the the tab for this module
=cut
@ -44,7 +44,7 @@ sub canView {
=head2 editSettingsForm ( )
Creates form elements for user settings page custom to this account module
Creates form elements for user settings page custom to this account module
=cut
@ -83,7 +83,7 @@ sub editSettingsForm {
=head2 editSettingsFormSave ( )
Creates form elements for the settings page custom to this account module
Save form elements from the settings.
=cut

View file

@ -18,6 +18,7 @@ use strict;
use WebGUI::AdSpace;
use WebGUI::Macro;
use WebGUI::Storage;
use WebGUI::AssetCollateral::Sku::Ad::Ad;
=head1 NAME
@ -81,6 +82,12 @@ Deletes this ad.
sub delete {
my $self = shift;
my $iterator = WebGUI::AssetCollateral::Sku::Ad::Ad->getAllIterator($self->session,{
constraints => [ { "adSkuPurchase.adId = ?" => $self->getId } ],
});
while( my $object = $iterator->() ) {
$object->update({'isDeleted' => 1});
}
my $storage = WebGUI::Storage->get($self->session, $self->get("storageId"));
$storage->delete if defined $storage;
$self->session->db->deleteRow("advertisement","adId",$self->getId);

View file

@ -930,7 +930,7 @@ sub getEditForm {
label => $i18n->get('keywords'),
hoverHelp => $i18n->get('keywords help'),
value => $self->get('keywords'),
fieldType => 'text',
fieldType => 'keywords',
tab => 'meta',
}
);
@ -2340,9 +2340,9 @@ sub update {
# next unless (exists $properties->{$property} || exists $definition->{properties}{$property}{defaultValue});
# skip a property unless it was specified to be set by the properties field
next unless (exists $properties->{$property});
my $propertyDefinition = $definition->{properties}{$property};
# skip a property if it has the display only flag set
next if ($definition->{properties}{$property}{displayOnly});
next if ($propertyDefinition->{displayOnly});
# skip properties that aren't yet in the table
if (!exists $tableFields{$property}) {
@ -2358,14 +2358,16 @@ sub update {
}
# apply filter logic on a property to validate or fix it's value
if (exists $definition->{properties}{$property}{filter}) {
my $filter = $definition->{properties}{$property}{filter};
$value = $self->$filter($value, $property);
}
if (exists $propertyDefinition->{filter}) {
my $filter = $propertyDefinition->{filter};
$value = $self->$filter($value, $property);
}
# use the default value because default and update were both undef
if ($value eq "" && exists $definition->{properties}{$property}{defaultValue}) {
$value = $definition->{properties}{$property}{defaultValue};
# if the value is undefined, use the default if possible
# unless allowEmpty has been set, do this for empty strings as well
if ( ( !defined $value || ( $value eq q{} && ! $propertyDefinition->{allowEmpty} ) )
&& exists $propertyDefinition->{defaultValue} ) {
$value = $propertyDefinition->{defaultValue};
if (ref($value) eq 'ARRAY') {
$value = $value->[0];
}
@ -2606,6 +2608,11 @@ NOTE: Don't try to override or overload this method. It won't work. What you are
sub www_editSave {
my $self = shift;
my $annotations = "";
if ($self->isa("WebGUI::Asset::File::Image")) {
$annotations = $self->get("annotations");
}
##If this is a new asset (www_add), the parent may be locked. We should still be able to add a new asset.
my $isNewAsset = $self->session->form->process("assetId") eq "new" ? 1 : 0;
return $self->session->privilege->locked() if (!$self->canEditIfLocked and !$isNewAsset);
@ -2644,6 +2651,12 @@ sub www_editSave {
}
}
if ($self->isa("WebGUI::Asset::File::Image")) {
$object->update({ annotations => $annotations });
}
###
$object->updateHistory("edited");
# we handle auto commit assets here in case they didn't handle it themselves

View file

@ -245,7 +245,6 @@ sub getEditFormUploadControl {
return $html;
}
#-------------------------------------------------------------------
sub getFileUrl {
my $self = shift;
@ -559,10 +558,11 @@ sub www_edit {
#-------------------------------------------------------------------
sub www_view {
my $self = shift;
return $self->session->privilege->noAccess() unless $self->canView;
# Check to make sure it's not in the trash or some other weird place
if ($self->get("state") ne "published") {
my $i18n = WebGUI::International->new($self->session,'Asset_File');

View file

@ -112,6 +112,10 @@ sub definition {
fieldType => 'textarea',
defaultValue => 'style="border-style:none;"',
},
annotations => {
fieldType => 'textarea',
defaultValue => '',
},
},
};
return $class->SUPER::definition($session,$definition);
@ -236,17 +240,32 @@ sub view {
return $out if $out;
}
my %var = %{$self->get};
my ($crop_js, $domMe) = $self->annotate_js({ just_image => 1 });
if ($crop_js) {
my ($style, $url) = $self->session->quick(qw(style url));
$style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/container/assets/container.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/container/container-min.js'), {type=>'text/javascript'});
}
$var{controls} = $self->getToolbar;
$var{fileUrl} = $self->getFileUrl;
$var{fileIcon} = $self->getFileIconUrl;
$var{thumbnail} = $self->getThumbnailUrl;
my $out = $self->processTemplate(\%var,undef,$self->{_viewTemplate});
$var{annotateJs} = "$crop_js$domMe";
$var{parameters} = sprintf("id=%s", $self->getId());
my $form = $self->session->form;
my $out = $self->processTemplate(\%var,undef,$self->{_viewTemplate});
if (!$self->session->var->isAdminOn && $self->get("cacheTimeout") > 10) {
WebGUI::Cache->new($self->session,"view_".$self->getId)->set($out,$self->get("cacheTimeout"));
}
return $out;
}
#----------------------------------------------------------------------------
=head2 setFile ( filename )
@ -268,6 +287,10 @@ sub www_edit {
return $self->session->privilege->locked() unless $self->canEditIfLocked;
my $i18n = WebGUI::International->new($self->session, 'Asset_Image');
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=resize'),$i18n->get("resize image")) if ($self->get("filename"));
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=rotate'),$i18n->get("rotate image")) if ($self->get("filename"));
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=crop'),$i18n->get("crop image")) if ($self->get("filename"));
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=annotate'),$i18n->get("annotate image")) if ($self->get("filename"));
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=undo'),$i18n->get("undo image")) if ($self->get("filename"));
my $tabform = $self->getEditForm;
$tabform->getTab("display")->template(
-value=>$self->get("templateId"),
@ -278,6 +301,283 @@ sub www_edit {
return $self->getAdminConsole->render($tabform->print,$i18n->get("edit image"));
}
#-------------------------------------------------------------------
sub www_undo {
my $self = shift;
my $previous = (@{$self->getRevisions()})[1];
# instantiate assetId
if ($previous) {
# my $session = $self->session;
# my $cache = WebGUI::Cache->new($self->session, ["asset",$self->getId,$self->getRevisionDate]);
# $cache->flush;
# my $cache = WebGUI::Cache->new($previous->session, ["asset",$previous->getId,$previous->getRevisionDate]);
# $cache->flush;
$self = $self->purgeRevision();
# $self = undef;
# $self = WebGUI::Asset->new($previous->session, $previous->getId, ref $previous, $previous->getRevisionDate);
$self = $previous;
$self->generateThumbnail;
}
return $self->www_edit();
}
#-------------------------------------------------------------------
#
# All of the images will have to change to support annotate.
# The revision system doesn't support the blobs, it seems.
# All of the image operations will have to be updated to support annotations.
#
sub www_annotate {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;
return $self->session->privilege->locked() unless $self->canEditIfLocked;
if (1) {
my $newSelf = $self->addRevision();
delete $newSelf->{_storageLocation};
$newSelf->getStorageLocation->annotate($newSelf->get("filename"),$newSelf,$newSelf->session->form);
$newSelf->setSize($newSelf->getStorageLocation->getFileSize($newSelf->get("filename")));
$self = $newSelf;
$self->generateThumbnail;
}
my ($style, $url) = $self->session->quick(qw(style url));
# $style->setLink($url->extras('annotate/imageMap.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/build/resize/assets/skins/sam/resize.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/build/imagecropper/assets/skins/sam/imagecropper.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/element/element-beta-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/dragdrop/dragdrop-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/resize/resize-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/imagecropper/imagecropper-beta-min.js'), {type=>'text/javascript'});
# my $imageAsset = $self->session->db->getRow("ImageAsset","assetId",$self->getId);
my @pieces = split(/\n/, $self->get('annotations'));
# my ($top_left, $width_height, $note) = split(/\n/, $imageAsset->{annotations});
my ($img_null, $tooltip_block, $tooltip_none) = ('', '', '');
for (my $i = 0; $i < $#pieces; $i += 3) {
$img_null .= "YAHOO.img.container.tt$i = null;\n";
$tooltip_block .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'block');\n";
$tooltip_none .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'none');\n";
my $j = $i + 2;
# warn("i: $i: ", $self->session->form->process("delAnnotate$i"));
}
my $image = '<div align="center" class="yui-skin-sam"><img src="'.$self->getStorageLocation->getUrl($self->get("filename")).'" style="border-style:none;" alt="'.$self->get("filename").'" id="yui_img" /></div>';
my ($width, $height) = $self->getStorageLocation->getSize($self->get("filename"));
my @checkboxes = ();
my $i18n = WebGUI::International->new($self->session,"Asset_Image");
my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
$f->hidden(
-name=>"func",
-value=>"annotate"
);
$f->text(
-label=>$i18n->get('annotate image'),
-value=>'',
-hoverHelp=>$i18n->get('annotate image description'),
-name=>'annotate_text'
);
$f->integer(
-label=>$i18n->get('top'),
-name=>"annotate_top",
-value=>,
);
$f->integer(
-label=>$i18n->get('left'),
-name=>"annotate_left",
-value=>,
);
$f->integer(
-label=>$i18n->get('width'),
-name=>"annotate_width",
-value=>,
);
$f->integer(
-label=>$i18n->get('height'),
-name=>"annotate_height",
-value=>,
);
$f->button(
-value=>$i18n->get('annotate'),
-extras=>'onclick="switchState();"',
);
$f->submit;
my ($crop_js, $domMe) = $self->annotate_js();
return $self->getAdminConsole->render($f->print."$image$crop_js$domMe",$i18n->get("annotate image"));
}
#-------------------------------------------------------------------
sub annotate_js {
my $self = shift;
my $opts = shift;
my @pieces = split(/\n/, $self->get('annotations'));
# warn("pieces: $#pieces: ". $self->getId());
return "" if !@pieces && $opts->{just_image};
my ($img_null, $tooltip_block, $tooltip_none) = ('', '', '');
for (my $i = 0; $i < $#pieces; $i += 3) {
$img_null .= "YAHOO.img.container.tt$i = null;\n";
$tooltip_block .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'block');\n";
$tooltip_none .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'none');\n";
my $j = $i + 2;
# warn("i: $i: ", $self->session->form->process("delAnnotate$i"));
}
my $id = $$opts{just_image} ? $self->getId : "yui_img";
my $crop_js = qq(
<script type="text/javascript">
var crop;
function switchState() {
$img_null
if (crop) {
crop.destroy();
crop = null;
$tooltip_block
}
else {
crop = new YAHOO.widget.ImageCropper('$id', {
initialXY: [20, 20],
keyTick: 5,
shiftKeyTick: 50
});
crop.on('moveEvent', function() {
var region = crop.getCropCoords();
element = document.getElementById('annotate_width_formId');
element.value = region.width;
element = document.getElementById('annotate_height_formId');
element.value = region.height;
element = document.getElementById('annotate_top_formId');
element.value = region.top;
element = document.getElementById('annotate_left_formId');
element.value = region.left;
});
$tooltip_none
}
}
</script>
);
my $hotspots = '';
my $domMe = '';
for (my $i = 0; $i < $#pieces; $i += 3) {
my $top_left = $pieces[$i];
my $width_height = $pieces[$i + 1];
my $note = $pieces[$i + 2];
if ($top_left =~ /top: (\d+)px; left: (\d+)px;/) {
$top_left = "xy[0]+$1, xy[1]+$2";
}
my ($width, $height) = ("", "");
if ($width_height =~ /width: (\d+)px; height: (\d+)px;/) {
($width, $height) = ("$1px", "$2px");
}
# next if 3 == $i;
warn('next');
$domMe .= qq(
<style type="text/css">
div#tooltip$i { position: absolute; border:1px solid; }
</style>
<span id=span_tooltip$i>
</span>
<script type="text/javascript">
function on_load_$i() {
var xy = YAHOO.util.Dom.getXY('$id');
document.getElementById('span_tooltip$i').innerHTML = "<div id=tooltip$i style='border:1px solid;'></div>";
YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'block');
YAHOO.util.Dom.setStyle('tooltip$i', 'height', '$height');
YAHOO.util.Dom.setStyle('tooltip$i', 'width', '$width');
YAHOO.util.Dom.setXY('span_tooltip$i', [$top_left]);
YAHOO.util.Dom.setXY('tooltip$i', [$top_left]);
YAHOO.namespace("img.container");
YAHOO.img.container.tt$i = new YAHOO.widget.Tooltip("tt$i", { showdelay: 0, visible: true, context:"tooltip$i", position:"relative", container:"tooltip$i", text:"$note" });
}
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", on_load_$i, false);
}
else if (window.attachEvent){
window.attachEvent('onload', on_load_$i);
}
</script>
);
}
return($crop_js, $domMe);
}
#-------------------------------------------------------------------
sub www_rotate {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;
return $self->session->privilege->locked() unless $self->canEditIfLocked;
# warn(sprintf("Rotate_formId: %s", $self->session->form->process("Rotate")));
if (defined $self->session->form->process("Rotate")) {
my $newSelf = $self->addRevision();
delete $newSelf->{_storageLocation};
$newSelf->getStorageLocation->rotate($newSelf->get("filename"),$newSelf->session->form->process("Rotate"));
$newSelf->setSize($newSelf->getStorageLocation->getFileSize($newSelf->get("filename")));
$self = $newSelf;
$self->generateThumbnail;
}
my ($x, $y) = $self->getStorageLocation->getSizeInPixels($self->get("filename"));
##YUI specific datatable CSS
my ($style, $url) = $self->session->quick(qw(style url));
my $img_name = $self->getStorageLocation->getUrl($self->get("filename"));
my $img_file = $self->get("filename");
my $image = '<div align="center" class="yui-skin-sam"><img src="'.$self->getStorageLocation->getUrl($self->get("filename")).'" style="border-style:none;" alt="'.$self->get("filename").'" id="yui_img" /></div>';
my $i18n = WebGUI::International->new($self->session,"Asset_Image");
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
$f->hidden(
-name=>"func",
-value=>"rotate"
);
$f->button(
-value=>"Left",
-extras=>qq(onclick="var deg = document.getElementById('Rotate_formId').value; deg = parseInt(deg) + 90; document.getElementById('Rotate_formId').value = deg;"),
);
$f->button(
-value=>"Right",
-extras=>qq(onclick="var deg = document.getElementById('Rotate_formId').value; deg = parseInt(deg) - 90; document.getElementById('Rotate_formId').value = deg;"),
);
$f->integer(
-label=>$i18n->get('degree'),
-name=>"Rotate",
-value=>0,
);
$f->submit;
return $self->getAdminConsole->render($f->print.$image,$i18n->get("rotate image"));
}
#-------------------------------------------------------------------
sub www_resize {
my $self = shift;
@ -289,7 +589,59 @@ sub www_resize {
$newSelf->getStorageLocation->resize($newSelf->get("filename"),$newSelf->session->form->process("newWidth"),$newSelf->session->form->process("newHeight"));
$newSelf->setSize($newSelf->getStorageLocation->getFileSize($newSelf->get("filename")));
$self = $newSelf;
$self->generateThumbnail;
}
my ($x, $y) = $self->getStorageLocation->getSizeInPixels($self->get("filename"));
##YUI specific datatable CSS
my ($style, $url) = $self->session->quick(qw(style url));
$style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/build/resize/assets/skins/sam/resize.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/element/element-beta-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/dragdrop/dragdrop-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/resize/resize-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/animation/animation-min.js'), {type=>'text/javascript'});
my $resize_js = qq(
<script>
(function() {
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event;
var resize = new YAHOO.util.Resize('yui_img', {
handles: 'all',
knobHandles: true,
height: '${x}px',
width: '${y}px',
proxy: true,
ghost: true,
status: true,
draggable: false,
ratio: true,
animate: true,
animateDuration: .75,
animateEasing: YAHOO.util.Easing.backBoth
});
resize.on('startResize', function() {
this.getProxyEl().innerHTML = '<img src="' + this.get('element').src + '" style="height: 100%; width: 100%;">';
Dom.setStyle(this.getProxyEl().firstChild, 'opacity', '.25');
}, resize, true);
resize.on('resize', function(e) {
element = document.getElementById('newWidth_formId');
element.value = e.width;
element = document.getElementById('newHeight_formId');
element.value = e.height;
}, resize, true);
})();
</script>
);
my $i18n = WebGUI::International->new($self->session,"Asset_Image");
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
@ -297,7 +649,6 @@ sub www_resize {
-name=>"func",
-value=>"resize"
);
my ($x, $y) = $self->getStorageLocation->getSizeInPixels($self->get("filename"));
$f->readOnly(
-label=>$i18n->get('image size'),
-hoverHelp=>$i18n->get('image size description'),
@ -316,15 +667,121 @@ sub www_resize {
-value=>$y,
);
$f->submit;
my $image = '<div align="center"><img src="'.$self->getStorageLocation->getUrl($self->get("filename")).'" style="border-style:none;" alt="'.$self->get("filename").'" /></div>';
my $image = '<div align="center" class="yui-skin-sam"><img src="'.$self->getStorageLocation->getUrl($self->get("filename")).'" style="border-style:none;" alt="'.$self->get("filename").'" id="yui_img" /></div>'.$resize_js;
return $self->getAdminConsole->render($f->print.$image,$i18n->get("resize image"));
}
#-------------------------------------------------------------------
sub www_crop {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;
return $self->session->privilege->locked() unless $self->canEditIfLocked;
if ($self->session->form->process("Width") || $self->session->form->process("Height")
|| $self->session->form->process("Top") || $self->session->form->process("Left")) {
my $newSelf = $self->addRevision();
delete $newSelf->{_storageLocation};
$newSelf->getStorageLocation->crop(
$newSelf->get("filename"),
$newSelf->session->form->process("Width"),
$newSelf->session->form->process("Height"),
$newSelf->session->form->process("Top"),
$newSelf->session->form->process("Left")
);
$self = $newSelf;
$self->generateThumbnail;
}
my $filename = $self->get("filename");
##YUI specific datatable CSS
my ($style, $url) = $self->session->quick(qw(style url));
my $crop_js = qq(
<script>
(function() {
var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event, results = null;
Event.onDOMReady(function() {
var crop = new YAHOO.widget.ImageCropper('yui_img', {
initialXY: [20, 20],
keyTick: 5,
shiftKeyTick: 50
});
crop.on('moveEvent', function() {
var region = crop.getCropCoords();
element = document.getElementById('Width_formId');
element.value = region.width;
element = document.getElementById('Height_formId');
element.value = region.height;
element = document.getElementById('Top_formId');
element.value = region.top;
element = document.getElementById('Left_formId');
element.value = region.left;
});
});
})();
</script>
);
$style->setLink($url->extras('yui/build/resize/assets/skins/sam/resize.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setLink($url->extras('yui/build/imagecropper/assets/skins/sam/imagecropper.css'), {rel=>'stylesheet', type=>'text/css'});
$style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/element/element-beta-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/dragdrop/dragdrop-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/resize/resize-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/imagecropper/imagecropper-beta-min.js'), {type=>'text/javascript'});
my $i18n = WebGUI::International->new($self->session,"Asset_Image");
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
$f->hidden(
-name=>"degree",
-value=>"0"
);
$f->hidden(
-name=>"func",
-value=>"crop"
);
my ($x, $y) = $self->getStorageLocation->getSizeInPixels($filename);
$f->integer(
-label=>$i18n->get('width'),
-hoverHelp=>$i18n->get('new width description'),
-name=>"Width",
-value=>$x,
);
$f->integer(
-label=>$i18n->get('height'),
-hoverHelp=>$i18n->get('new height description'),
-name=>"Height",
-value=>$y,
);
$f->integer(
-label=>$i18n->get('top'),
-hoverHelp=>$i18n->get('new width description'),
-name=>"Top",
-value=>$x,
);
$f->integer(
-label=>$i18n->get('left'),
-hoverHelp=>$i18n->get('new height description'),
-name=>"Left",
-value=>$y,
);
$f->submit;
my $image = '<div align="center" class="yui-skin-sam"><img src="'.$self->getStorageLocation->getUrl($filename).'" style="border-style:none;" alt="'.$filename.'" id="yui_img" /></div>'.$crop_js;
return $self->getAdminConsole->render($f->print.$image,$i18n->get("crop image"));
}
#-------------------------------------------------------------------
# Use superclass method for now.
sub www_view {
my $self = shift;
$self->SUPER::www_view;
return($self->SUPER::www_view);
}
#sub www_view {

View file

@ -45,6 +45,22 @@ These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 unzip ( $storage, $filename )
Uncompress and/or expand an archive, based on the file extension of the filename.
Returns 1 if the unzip was successful. Returns 0 if there were problems.
=head3 $storage
A WebGUI::Storage object containing the archive.
=head3 $filename
The filename of the archive.
=cut
sub unzip {
my $self = shift;
my $storage = shift;
@ -78,8 +94,8 @@ sub unzip {
=head2 addRevision ( )
This method exists for demonstration purposes only. The superclass
handles revisions to ZipArchive Assets.
This method exists for demonstration purposes only. The superclass
handles revisions to ZipArchive Assets.
=cut

View file

@ -304,6 +304,14 @@ sub getEditForm {
-hoverHelp =>$i18n->get("description description"),
-value =>$self->getValue('description'),
);
if ($self->getParent->canEdit) {
$form->user(
name =>"ownerUserId",
value =>$self->getValue('ownerUserId'),
label =>$i18n->get('maintainer label'),
hoverHelp =>$i18n->get('maintainer description'),
);
}
$form->text(
-name =>'version',
-defaultValue =>undef,
@ -335,21 +343,17 @@ sub getEditForm {
foreach my $category (keys %{$self->getParent->getCategories}) {
$form->raw('<tr><td colspan="2"><b>'.$category.'</b></td></tr>');
my $attributes;
if ($session->form->process('func') eq 'add'){
$attributes = $db->read("select * from Matrix_attribute where category = ? and assetId = ?",
[$category,$matrixId]);
}
else{
$attributes = $db->read("select * from Matrix_attribute as attribute
left join MatrixListing_attribute as listing using(attributeId)
where listing.matrixListingId = ? and category =? and attribute.assetId = ?",
[$self->getId,$category,$matrixId]);
}
my $attributes = $db->read("select * from Matrix_attribute where category = ? and assetId = ?",
[$category,$matrixId]);
while (my $attribute = $attributes->hashRef) {
$attribute->{label} = $attribute->{name};
$attribute->{subtext} = $attribute->{description};
$attribute->{name} = 'attribute_'.$attribute->{attributeId};
unless($session->form->process('func') eq 'add'){
$attribute->{value} = $db->quickScalar("select value from MatrixListing_attribute
where attributeId = ? and matrixId = ? and matrixListingId = ?",
[$attribute->{attributeId},$matrixId,$self->getId]);
}
if($attribute->{fieldType} eq 'Combo'){
my %options;
tie %options, 'Tie::IxHash';
@ -559,7 +563,7 @@ sub setRatings {
my $half = round($count/2);
my $mean = $sum / ($count || 1);
my $median = $db->quickScalar("select rating $sql limit $half,$half",[$self->getId,$category]);
my $median = $db->quickScalar("select rating $sql order by rating limit $half,1",[$self->getId,$category]);
$db->write("replace into MatrixListing_ratingSummary
(listingId, category, meanValue, medianValue, countValue, assetId)
@ -570,7 +574,7 @@ sub setRatings {
#-------------------------------------------------------------------
=head2 view ( hasRated )
=head2 updateScore ( )
Updates the score of a MatrixListing.

View file

@ -1,205 +0,0 @@
package WebGUI::Asset::RSSCapable;
=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 NEXT;
use WebGUI::Asset::RSSFromParent;
=head1 NAME
WebGUI::Asset::RSSCapable
=head1 DESCRIPTION
An extra mixin class to be included before WebGUI::Asset in any asset
class that wishes its instances to be capable of generating RSS feeds
using the RSSFromParent asset.
=head1 SYNOPSIS
use base 'WebGUI::Asset::RSSCapable';
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my %properties;
tie %properties, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session, 'Asset_RSSCapable');
# We do this prefixing to avoid name collisions because properties aren't namespaced.
%properties =
(
rssCapableRssEnabled => { tab => 'display',
fieldType => 'yesNo',
defaultValue => 1,
label => $i18n->get('rssEnabled label'),
hoverHelp => $i18n->get('rssEnabled hoverHelp')
},
rssCapableRssTemplateId => { tab => 'display',
fieldType => 'template',
defaultValue => 'PBtmpl0000000000000142',
namespace => 'RSSCapable/RSS',
label => $i18n->get('rssTemplateId label'),
hoverHelp => $i18n->get('rssTemplateId hoverHelp')
},
rssCapableRssFromParentId => { fieldType => 'hidden',
noFormPost => 1,
defaultValue => undef,
},
);
push @$definition, { assetName => $i18n->get('assetName'),
tableName => 'RSSCapable',
autoGenerateForms => 1,
className => 'WebGUI::Asset::RSSCapable',
icon => 'rssCapable.gif',
properties => \%properties
};
return $class->NEXT::definition($session, $definition);
}
#-------------------------------------------------------------------
sub _rssFromParentValid {
my $self = shift;
my $rssFromParentId = $self->get('rssCapableRssFromParentId');
return undef unless $rssFromParentId;
my $rssFromParent = WebGUI::Asset->newByDynamicClass($self->session, $rssFromParentId);
return undef unless $rssFromParent;
return ($rssFromParent->isa('WebGUI::Asset::RSSFromParent')
&& $rssFromParent->getParent->getId eq $self->getId);
}
#-------------------------------------------------------------------
sub _updateRssFromParentProperties {
my $self = shift;
my $rssFromParent = WebGUI::Asset->newByDynamicClass($self->session,
$self->get('rssCapableRssFromParentId'));
$rssFromParent->update({ title => $self->get('title'),
menuTitle => $self->get('menuTitle') });
}
#-------------------------------------------------------------------
sub _purgeExtraRssFromParentAssets {
my $self = shift;
my $rssFromParentId = $self->get('rssCapableRssFromParentId');
foreach my $rssFromParent (@{$self->getLineage(['children'],
{returnObjects => 1,
includeOnlyClasses =>
['WebGUI::Asset::RSSFromParent']})}) {
$rssFromParent->purge unless $rssFromParent->getId eq $rssFromParentId;
}
}
#-------------------------------------------------------------------
sub _ensureRssFromParentPresent {
my $self = shift;
if (!$self->_rssFromParentValid) {
# Create a new one.
my $rssFromParent = $self->addChild({ className => 'WebGUI::Asset::RSSFromParent',
title => $self->get('title'),
menuTitle => $self->get('menuTitle'),
url => $self->get('url').'.rss'
});
$self->update({ rssCapableRssFromParentId => $rssFromParent->getId });
}
$self->_updateRssFromParentProperties;
$self->_purgeExtraRssFromParentAssets;
}
#-------------------------------------------------------------------
sub _ensureRssFromParentAbsent {
my $self = shift;
# Invalidate it, and then it'll get purged along with any others.
$self->update({ rssCapableRssFromParentId => undef });
$self->_purgeExtraRssFromParentAssets;
}
#-------------------------------------------------------------------
sub processPropertiesFromFormPost {
my $self = shift;
my $error = $self->NEXT::processPropertiesFromFormPost(@_);
return $error if ref $error eq 'ARRAY';
if ($self->get('rssCapableRssEnabled')) {
$self->_ensureRssFromParentPresent;
} else {
$self->_ensureRssFromParentAbsent;
}
return undef;
}
#-------------------------------------------------------------------
=head2 getRssUrl ( )
Returns the site-relative URL to the RSS feed for this asset, or undef
if there is no such feed.
=cut
sub getRssUrl {
my $self = shift;
my $rssFromParentId = $self->get('rssCapableRssFromParentId');
return undef unless $rssFromParentId;
my $rssAsset = WebGUI::Asset->newByDynamicClass($self->session, $rssFromParentId);
return undef unless $rssAsset;
return $rssAsset->getUrl;
}
#-------------------------------------------------------------------
=head2 getRssItems ( )
Returns a list of RSS items for a feed corresponding to this asset.
Each item may be another asset, or a hash of (properly XMLized)
properties for the <item>..</item> tag. Defaults to no items.
This is the primary method that RSSCapable assets should override.
=cut
sub getRssItems { () }
#-------------------------------------------------------------------
=head2 www_viewRSS ( )
Default www method for methods that return RSS. This will redirect to the getRssUrl unless overridden.
=cut
sub www_viewRSS {
my $self = shift;
my $session = $self->session;
my $rssUrl = $self->getRssUrl;
if($rssUrl) {
$session->http->setRedirect($self->getRssUrl);
}
return undef;
}
1;

View file

@ -1,174 +0,0 @@
package WebGUI::Asset::RSSFromParent;
=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 HTML::Entities;
use Tie::IxHash;
use base 'WebGUI::Asset';
use WebGUI::Utility;
=head1 NAME
Package WebGUI::Asset::RSSFromParent
=head1 DESCRIPTION
Generates an RSS feed from the children/descendants of its parent.
=head1 SYNOPSIS
use WebGUI::Asset::RSSFromParent;
=cut
#-------------------------------------------------------------------
=head2 definition
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my %properties;
tie %properties, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session, "Asset_RSSFromParent");
%properties = ();
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
icon=>'rssGear.gif',
autoGenerateForms=>1,
tableName=>'RSSFromParent',
className=>'WebGUI::Asset::RSSFromParent',
properties=>\%properties
});
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 update
=cut
sub update {
# Re-force isHidden to 1 on each update; these should always be hidden.
my $self = shift;
my $properties = shift;
$self->SUPER::update(+{%$properties, isHidden => 1});
}
#------------------------------------------------
=head2 _escapeXml
=cut
sub _escapeXml {
my $text = shift;
return $text unless (ref $text eq "");
return HTML::Entities::encode_numeric($text)
}
#------------------------------------------------
=head2 _tlsOfAsset
=cut
sub _tlsOfAsset {
my $self = shift;
my $asset = shift;
#Fix Title
my $title = _escapeXml($asset->get('title'));
#Fix Url
my $url = _escapeXml($self->session->url->getSiteURL() . $asset->getUrl);
#Fix Description
my $description = _escapeXml($asset->get('synopsis'));
return ($title,$url,$description);
}
#------------------------------------------------
=head2 {
=cut
sub isValidRssItem { 0 }
#------------------------------------------------
=head2 displayInFolder2
=cut
sub displayInFolder2 { 0 }
#------------------------------------------------
=head2 www_view
=cut
sub www_view {
my $self = shift;
return '' unless $self->session->asset->getId eq $self->getId;
return '' unless $self->getParent->isa('WebGUI::Asset::RSSCapable');
return '' unless $self->getParent->canView; # Go to parent for auth
my $parent = $self->getParent;
my $template = WebGUI::Asset::Template->new($self->session, $parent->get('rssCapableRssTemplateId'));
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->session->http->setMimeType('text/xml');
my $var = {};
@$var{'title', 'link', 'description'} = $self->_tlsOfAsset($parent);
$var->{'generator'} = "WebGUI $WebGUI::VERSION";
$var->{'lastBuildDate'} = $self->session->datetime->epochToMail($parent->getContentLastModified);
$var->{'webMaster'} = $self->session->setting->get('companyEmail');
$var->{'docs'} = 'http://blogs.law.harvard.edu/tech/rss';
my @items = $parent->getRssItems;
$var->{'item_loop'} = [];
my $counter = 0;
foreach my $item (@items) {
my $subvar = {};
if (UNIVERSAL::isa($item, 'WebGUI::Asset')) {
next unless $item->isValidRssItem;
$subvar = {};
@$subvar{'title', 'link', 'description'} = $self->_tlsOfAsset($item);
$subvar->{guid} = $subvar->{link};
$subvar->{pubDate} = _escapeXml($self->session->datetime->epochToMail($item->get('creationDate')));
} elsif (ref $item eq 'HASH') {
foreach my $key (keys %$item) {
$subvar->{$key} = _escapeXml($item->{$key});
}
} else {
$self->session->errorHandler->error("Don't know what to do with this RSS item: $item");
next;
}
$counter++;
push @{$var->{'item_loop'}}, $subvar;
}
return $self->processTemplate($var, undef, $template);
}
1;

View file

@ -126,14 +126,6 @@ sub view {
}
}
#-------------------------------------------------------------------
sub www_edit {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;
return $self->session->privilege->locked() unless $self->canEditIfLocked;
return $self->getAdminConsole->render($self->getEditForm->print, $self->addEditLabel);
}
#-------------------------------------------------------------------
=head2 www_view

View file

@ -448,6 +448,18 @@ sub getToolbar {
#-------------------------------------------------------------------
=head2 getRichEditor ( $nameId )
Return the javascript needed to make the Rich Editor.
=head3 $nameId
The id for the rich editor, should be unique enough to be used as the id parameter
for a HTML tag.
=cut
sub getRichEditor {
my $self = shift;
return '' if ($self->getValue('disableRichEditor'));
@ -582,6 +594,13 @@ sub indexContent {
#-------------------------------------------------------------------
=head2 www_edit ( )
Override the method from Asset.pm to change the title of the screen.
=cut
sub www_edit {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;

View file

@ -159,6 +159,27 @@ sub definition {
}
#-------------------------------------------------------------------
=head2 getAddToCartForm ( )
Returns a form to add this Sku to the cart. Used when this Sku is part of
a shelf.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Sku');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'addToCart'})
. WebGUI::Form::submit( $session, {value => $i18n->get('add to cart')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getCart ( )
@ -293,6 +314,7 @@ sub getThumbnailUrl {
}
#-------------------------------------------------------------------
=head2 getVendorId ( )
Returns the vendorId of the vendor for this sku. Defaults to the default

638
lib/WebGUI/Asset/Sku/Ad.pm Normal file
View file

@ -0,0 +1,638 @@
package WebGUI::Asset::Sku::Ad;
=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 Tie::IxHash;
use base 'WebGUI::Asset::Sku';
use WebGUI::Asset::Template;
use WebGUI::Form;
use WebGUI::Storage;
use WebGUI::Shop::Pay;
use WebGUI::AssetCollateral::Sku::Ad::Ad;
use WebGUI::AdSpace;
use WebGUI::AdSpace::Ad;
=head1 NAME
Package WebGUI::Asset::Sku::Ad
=head1 DESCRIPTION
This Asset allows ads to be purchased via WebGUI shopping
=head1 SYNOPSIS
use WebGUI::Asset::Sku::Ad;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition
Adds purchaseTemplate, manageTemplate, adSpace, priority, pricePerClick, pricePerImpression, clickDiscounts, impresisonDiscounts
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my %properties;
tie %properties, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session, "Asset_AdSku");
%properties = (
purchaseTemplate => {
tab => "display",
fieldType => "template",
namespace => "AdSku/Purchase",
defaultValue => 'AldPGu0u-jm_5xK13atCSQ',
label => $i18n->get("property purchase template"),
hoverHelp => $i18n->get("property purchase template help"),
},
manageTemplate => {
tab => "display",
fieldType => "template",
namespace => "AdSku/Manage",
defaultValue => 'ohjyzab5i-yW6GOWTeDUHg',
label => $i18n->get("property manage template"),
hoverHelp => $i18n->get("property manage template help"),
},
adSpace => {
tab => "properties",
fieldType => "AdSpace",
namespace => "AdSku",
label => $i18n->get("property ad space"),
hoverHelp => $i18n->get("property ad Space help"),
},
priority => {
tab => "properties",
defaultValue => '1',
fieldType => "integer",
label => $i18n->get("property priority"),
hoverHelp => $i18n->get("property priority help"),
},
pricePerClick => {
tab => "shop",
defaultValue => '0.00',
fieldType => "float",
label => $i18n->get("property price per click"),
hoverHelp => $i18n->get("property price per click help"),
},
pricePerImpression => {
tab => "shop",
defaultValue => '0.00',
fieldType => "float",
label => $i18n->get("property price per impression"),
hoverHelp => $i18n->get("property price per impression help"),
},
clickDiscounts => {
tab => "shop",
fieldType => 'textarea',
label => $i18n->get('property click discounts'),
hoverHelp => $i18n->get('property click discounts help'),
defaultValue => '',
},
impressionDiscounts => {
tab => "shop",
fieldType => 'textarea',
label => $i18n->get('property impression discounts'),
hoverHelp => $i18n->get('property impression discounts help'),
defaultValue => '',
},
);
# Show the karma field only if karma is enabled
if ($session->setting->get("useKarma")) {
$properties{ karma } = {
type => 'integer',
label => $i18n->get('property adsku karma'),
hoverHelp => $i18n->get('property adsku karma description'),
defaultvalue => 0,
};
}
push(@{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'adsku.gif',
autoGenerateForms => 1,
tableName => 'AdSku',
className => 'WebGUI::Asset::Sku::AdSku',
properties => \%properties,
});
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getAddToCartForm
Returns an empty string, since the add to cart form is complex.
=cut
sub getAddToCartForm {
return '';
}
#-------------------------------------------------------------------
=head2 getClickDiscountText
returns the text to display the number of clicks purchasaed where discounts apply
=cut
sub getClickDiscountText {
my $self = shift;
return getDiscountText($self->i18n->get('click discount'),
$self->get('clickDiscounts'));
}
#-------------------------------------------------------------------
=head2 getConfiguredTitle
combines the adSKu title with the customers ad title
=cut
sub getConfiguredTitle {
my $self = shift;
return $self->get('title') . ' (' . $self->getOptions->{'adtitle'} . ')';
}
#-------------------------------------------------------------------
=head2 getDiscountAmount -- class level function
returns the amount of discount to apply to this purchase
=cut
sub getDiscountAmount {
my($discounts,$count) = @_;
my @discounts = parseDiscountText( $discounts );
my $previousDiscount = 0;
foreach my $discountSet ( @discounts ) {
last if $count < $discountSet->[1];
$previousDiscount = $discountSet->[0];
}
return $previousDiscount;
}
#-------------------------------------------------------------------
=head2 getDiscountText -- class level function
returns a string with a coma seperated list of counts from the discount text
=cut
sub getDiscountText {
my($format,$discounts) = @_;
return sprintf( $format, join( ',', (map { $_->[1] } ( parseDiscountText( $discounts ) ) ) ) );
}
#-------------------------------------------------------------------
=head2 getImpressionDiscountText
returns the text to display the number of impressions purchased where discounts apply
=cut
sub getImpressionDiscountText {
my $self = shift;
return getDiscountText($self->i18n->get('impression discount'),
$self->get('impressionDiscounts'));
}
#-------------------------------------------------------------------
=head2 getPrice
get the price for this purchase
=cut
sub getPrice {
my $self = shift;
my $options = $self->getOptions;
my $impressionCount = $options->{impressions} || $self->{formImpressions};
my $clickCount = $options->{clicks};
my $impressionDiscount = getDiscountAmount($self->get('impressionDiscounts'),$impressionCount );
my $clickDiscount = getDiscountAmount($self->get('clickDiscounts'),$clickCount );
my $impressionPrice = $self->get('pricePerImpression') * ( 100 - $impressionDiscount ) / 100 ;
my $clickPrice = $self->get('pricePerClick') * ( 100 - $clickDiscount ) / 100 ;
return sprintf "%.2f", $impressionPrice * $impressionCount + $clickPrice * $clickCount;
}
#-------------------------------------------------------------------
=head2 i18n
returns an internationalization object for this class
=cut
sub i18n {
my $self = shift;
return WebGUI::International->new($self->session, "Asset_AdSku");
}
#-------------------------------------------------------------------
=head2 manage
generate template vars for manage page
=cut
sub manage {
my ($self) = @_;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, "Asset_AdSku");
my %var;
$var{purchaseLink} = $self->getUrl;
my $iterator = WebGUI::AssetCollateral::Sku::Ad::Ad->getAllIterator($session,{
constraints => [ { "adSkuPurchase.userId = ?" => $self->session->user->userId } ],
orderBy => 'dateOfPurchase',
});
my %ads;
while( my $object = $iterator->() ) {
next if $object->get('isDeleted');
next if exists $ads{$object->get('adId')};
my $ad = $ads{$object->get('adId')} = WebGUI::AdSpace::Ad->new($session,$object->get('adId'));
push @{$var{myAds}}, {
rowTitle => $ad->get('title'),
rowClicks => $ad->get('clicks') . '/' . $ad->get('clicksBought'),
rowImpressions => $ad->get('impressions') . '/' . $ad->get('impressionsBought'),
rowRenewLink => $self->getUrl('func=renew;Id=' . $object->get('adSkuPurchaseId') ),
};
}
return $self->processTemplate(\%var,undef,$self->{_viewTemplate});
}
#-------------------------------------------------------------------
=head2 onCompletePurchase
inserts the ad into the adspace...
=cut
sub onCompletePurchase {
my $self = shift;
my $item = shift;
my $options = $self->getOptions;
my $ad;
# LATER: if we use Temp Storage for the image we need to move it to perm storage
if( $options->{adId} ne '' ) {
$ad = WebGUI::AdSpace::Ad->new($self->session,$options->{adId});
my $clicks = $options->{clicks} + $ad->get('clicksBought');
my $impressions = $options->{impressions} + $ad->get('impressionsBought');
$ad->set({
title => $options->{'adtitle'},
clicksBought => $clicks,
impressionsBought => $impressions,
url => $options->{'link'},
storageId => $options->{'image'},
});
} else {
$ad = WebGUI::AdSpace::Ad->create($self->session,$self->get('adSpace'),{
title => $options->{'adtitle'},
clicksBought => $options->{'clicks'},
impressionsBought => $options->{'impressions'},
url => $options->{'link'},
storageId => $options->{'image'},
ownerUserId => $self->session->user->userId,
isActive => 1,
type => 'image',
priority => $self->get('priority'),
adSpace => $self->get('adSpace'),
});
}
WebGUI::AssetCollateral::Sku::Ad::Ad->create($self->session,{
userId => $item->transaction->get('userId'),
transactionItemId => $item->getId,
adId => $ad->getId,
clicksPurchased => $options->{'clicks'},
impressionsPurchased => $options->{'impressions'},
dateOfPurchase => $item->transaction->get('dateOfPurchase'),
storedImage => $options->{'image'},
isDeleted => 0,
});
}
#-------------------------------------------------------------------
=head2 onRemoveFromCart
deletes the image if it gets removed from the cart
LATER: if we switch to using Temp Storage we do not need to do this.
=cut
sub onRemoveFromCart {
my $self = shift;
my $item = shift;
my $options = $self->getOptions;
WebGUI::Storage->get($self->session,$options->{'image'})->delete;
}
#-------------------------------------------------------------------
=head2 onRefund
delete the add if it gets refunded
=cut
sub onRefund {
my $self = shift;
my $item = shift;
my $iterator = WebGUI::AssetCollateral::Sku::Ad::Ad->getAllIterator($self->session,{
constraints => [ { "transactionItemId = ?" => $item->getId } ],
});
my $crud = $iterator->();
my $ad = WebGUI::AdSpace::Ad->new($self->session,$crud->get('adId'));
my $clicks = $ad->get('clicksBought') - $crud->get('clicksPurchased');
my $impressions = $ad->get('impressionsBought') - $crud->get('impressionsPurchased') ;
$ad->set({
clicksBought => $clicks,
impressionsBought => $impressions,
});
$crud->delete;
}
#-------------------------------------------------------------------
=head2 parseDiscountText -- class level function
returns an array of array ref's that are extracted from the discount description text
=cut
sub parseDiscountText {
my $discountDescription = shift;
my @lines = split "\n", $discountDescription;
my @discounts;
foreach my $line ( @lines ) {
if( $line =~ /^(\d+)\@(\d+)/ ) {
push @discounts, [ $1, $2 ];
}
}
return sort { $a->[1] <=> $b->[1] } @discounts;
}
#-------------------------------------------------------------------
=head2 prepareManage
Prepares the template.
=cut
sub prepareManage {
my $self = shift;
$self->SUPER::prepareView();
my $templateId = $self->get("manageTemplate");
my $template = WebGUI::Asset::Template->new($self->session, $templateId);
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 prepareView
Prepares the template.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
my $templateId = $self->get("purchaseTemplate");
my $template = WebGUI::Asset::Template->new($self->session, $templateId);
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 view
Displays the purchase adspace form
=cut
sub view {
my ($self) = @_;
my $session = $self->session;
my $options = $self->getOptions();
my $i18n = WebGUI::International->new($session, "Asset_AdSku");
my %var = (
formHeader => WebGUI::Form::formHeader($session, { action=>$self->getUrl })
. WebGUI::Form::hidden( $session, { name=>"func", value=>"addToCart" }),
formFooter => WebGUI::Form::formFooter($session),
formSubmit => WebGUI::Form::submit( $session, { value => $i18n->get("form purchase button") }),
error_msg => $options->{error_msg},
hasAddedToCart => $self->{_hasAddedToCart},
continueShoppingUrl => $self->getUrl,
manageLink => $self->getUrl("func=manage"),
adSkuTitle => $self->get('title'),
adSkuDescription => $self->get('description'),
formTitle => WebGUI::Form::text($session, {
-name=>"formTitle",
-value=>$options->{adtitle},
-size=>40
-default=>'untitled',
}),
formLink => WebGUI::Form::Url($session, {
-name=>"formLink",
-value=>$options->{link},
-size=>40
-required=>1,
}),
formImage => WebGUI::Form::File($session, {
-name=>"formImage",
-value=>$options->{image},
-size=>40
-forceImageOnly=>1,
}),
formClicks => WebGUI::Form::Integer($session, {
-name=>"formClicks",
-value=>$options->{clicks},
-size=>40
-required=>1,
}),
formImpressions => WebGUI::Form::Integer($session, {
-name=>"formImpressions",
-value=>$options->{impressions},
-size=>40
-required=>1,
}),
formAdId => WebGUI::Form::Hidden($session, {
-name=>"formAdId",
-value=>$options->{adId} || '',
}),
clickPrice => $self->get('pricePerClick'),
impressionPrice => $self->get('pricePerImpression'),
clickDiscount => $self->getClickDiscountText,
impressionDiscount => $self->getImpressionDiscountText,
);
return $self->processTemplate(\%var,undef,$self->{_viewTemplate});
}
#-------------------------------------------------------------------
=head2 www_addToCart
Add this subscription to the cart.
=cut
sub www_addToCart {
my $self = shift;
my $session = $self->session;
my $i18n = $self->i18n;
if ($self->canView) {
my $form = $session->form;
my @errors;
#my $imageStorage = $self->getOptions->{image} || WebGUI::Storage->create($session); # LATER should be createTemp
my $imageStorageId = $form->process('formImage', 'image'); # , $self->getOptions->{image});
my $imageStorage = WebGUI::Storage->get($session,$imageStorageId);
my $code;
if( not defined $imageStorage ) { $code = 1; }
elsif( $imageStorage->getErrorCount > 0 ) { $code = 2; }
elsif( scalar(@{$imageStorage->getFiles}) == 0 ) { $code = 3; }
elsif( $imageStorage->isImage((@{$imageStorage->getFiles})[0]) ) { $code = 4; }
if( not defined $imageStorage
or $imageStorage->getErrorCount > 0
or scalar(@{$imageStorage->getFiles}) == 0
# or $imageStorage->isImage((@{$imageStorage->getFiles})[0]) # not currently working
) {
push @errors, $i18n->get('form error no image') . $code . eval { (@{$imageStorage->getFiles})[0] } ;
}
my $title = $form->process('formTitle');
if($title eq '' ) {
push @errors, $i18n->get('form error no title');
}
my $link = $form->process('formLink','url');
if($link eq '' ) {
push @errors, $i18n->get('form error no link');
}
my $adId = $self->get('adSpace');
my $adSpace = WebGUI::AdSpace->new($session,$adId);
my $clicks = $form->process('formClicks','integer');
if($clicks < $adSpace->get('minimumClicks') ) {
push @errors, sprintf($i18n->get('form error min clicks'), $adSpace->get('minimumClicks'));
}
my $impressions = $form->process('formImpressions','integer');
if($impressions < $adSpace->get('minimumImpressions') ) {
push @errors, sprintf($i18n->get('form error min impressions'), $adSpace->get('minimumImpressions'));
}
if( @errors == 0 ) {
$self->{_hasAddedToCart} = 1;
$self->addToCart({
adtitle => $title,
link => $link,
clicks => $clicks,
impressions => $impressions,
adId => $adId,
image => $imageStorageId,
});
} else {
$self->applyOptions({
adtitle => $title,
link => $link,
clicks => $clicks,
impressions => $impressions,
adId => $adId,
image => $imageStorageId,
error_msg => join( '<br>', @errors ),
});
}
}
return $self->www_view;
}
#-------------------------------------------------------------------
=head2 www_manage
manage previously purchased ads
=cut
sub www_manage {
my $self = shift;
my $check = $self->checkView;
return $check if (defined $check);
$self->session->http->setLastModified($self->getContentLastModified);
$self->session->http->sendHeader;
$self->prepareManage;
my $style = $self->processStyle($self->getSeparator);
my ($head, $foot) = split($self->getSeparator,$style);
$self->session->output->print($head, 1);
$self->session->output->print($self->manage);
$self->session->output->print($foot, 1);
return "chunked";
}
#-------------------------------------------------------------------
=head2 www_renew
renew an ad
=cut
sub www_renew {
my $self = shift;
my $session = $self->session;
my $id = $session->form->get('Id');
my $crud = WebGUI::AssetCollateral::Sku::Ad::Ad->new($session,$id);
my $ad = WebGUI::AdSpace::Ad->new($session,$crud->get('adId'));
$self->applyOptions({
adtitle => $ad->get('title'),
clicks => $crud->get('clicksPurchased'),
impressions => $crud->get('impressionsPurchased'),
link => $ad->get('url'),
image => $ad->get('storageId'),
adId => $crud->get('adId'),
});
return $self->www_view;
}
1;

View file

@ -92,6 +92,28 @@ sub definition {
}
#-------------------------------------------------------------------
=head2 getAddToCartForm ( )
Returns a form to add this Sku to the cart. Used when this Sku is part of
a shelf. Overrode master class to add price form.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Donation');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'donate'})
. WebGUI::Form::float( $session, {name => 'price', defaultValue => $self->getPrice })
. WebGUI::Form::submit( $session, {value => $i18n->get('donate button')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getConfiguredTitle
@ -162,18 +184,21 @@ sub view {
#-------------------------------------------------------------------
=head2 wwww_donate
=head2 www_donate
Accepts the information from the donation form and adds it to the cart.
=cut
sub www_donate {
my $self = shift;
if ($self->canView) {
my $self = shift;
my $price = $self->session->form->get("price") || $self->getPrice;
if ($self->canView && $price > 0) {
$self->{_hasAddedToCart} = 1;
$self->addToCart({price => ($self->session->form->get("price") || $self->getPrice) });
$self->addToCart( { price => $price } );
}
return $self->www_view;
}

View file

@ -165,6 +165,26 @@ sub drawRelatedBadgeGroupsField {
}
#-------------------------------------------------------------------
=head2 getAddToCartForm
Returns a button to take the user to the view screen.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Sku');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'view'})
. WebGUI::Form::submit( $session, {value => $i18n->get('see more')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getConfiguredTitle

View file

@ -83,6 +83,26 @@ sub definition {
}
#-------------------------------------------------------------------
=head2 getAddToCartForm
Returns a button to take the user to the view screen.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Sku');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'view'})
. WebGUI::Form::submit( $session, {value => $i18n->get('see more')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getConfiguredTitle

View file

@ -229,6 +229,25 @@ sub drawRelatedRibbonsField {
#-------------------------------------------------------------------
=head2 getAddToCartForm
Returns a button to take the user to the view screen.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Sku');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'view'})
. WebGUI::Form::submit( $session, {value => $i18n->get('see more')})
. WebGUI::Form::formFooter($session)
;}
#-------------------------------------------------------------------
=head2 getConfiguredTitle
Returns title + badgeholder name.

View file

@ -76,6 +76,26 @@ sub definition {
}
#-------------------------------------------------------------------
=head2 getAddToCartForm
Returns a button to take the user to the view screen.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Sku');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'view'})
. WebGUI::Form::submit( $session, {value => $i18n->get('see more')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getConfiguredTitle

View file

@ -242,7 +242,7 @@ sub view {
#-------------------------------------------------------------------
=head2 wwww_addToCart
=head2 www_addToCart
Accepts the information from the form and adds it to the cart.

View file

@ -240,6 +240,37 @@ sub duplicate {
}
#-------------------------------------------------------------------
=head2 getAddToCartForm ( )
Returns a form to add this Sku to the cart. Used when this Sku is part of
a shelf. Overrode master class to add variant dropdown.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Product');
my %variants = ();
tie %variants, 'Tie::IxHash';
foreach my $collateral ( @{ $self->getAllCollateral('variantsJSON')} ) {
$variants{$collateral->{variantId}} = join ", ", $collateral->{shortdesc}, sprintf('%.2f',$collateral->{price});
}
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'buy'})
. WebGUI::Form::selectBox( $session, {
name => 'vid',
options => \%variants,
value => [0],
})
. WebGUI::Form::submit( $session, {value => $i18n->get('add to cart')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getAllCollateral ( tableName )

View file

@ -269,6 +269,27 @@ sub generateSubscriptionCodeBatch {
#-------------------------------------------------------------------
=head2 getAddToCartForm ( )
Returns a form to add this Sku to the cart. Used when this Sku is part of
a shelf. Override master class to add different form.
=cut
sub getAddToCartForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Subscription');
return
WebGUI::Form::formHeader($session, {action => $self->getUrl})
. WebGUI::Form::hidden( $session, {name => 'func', value => 'purchaseSubscription'})
. WebGUI::Form::submit( $session, {value => $i18n->get('purchase button')})
. WebGUI::Form::formFooter($session)
;
}
#-------------------------------------------------------------------
=head2 getAdminConsoleWithSubmenu ( )
Returns an admin console with management links added to the submenu.
@ -933,7 +954,7 @@ sub www_listSubscriptionCodes {
#-------------------------------------------------------------------
=head2 wwww_purchaseSubscription
=head2 www_purchaseSubscription
Add this subscription to the cart.

View file

@ -191,6 +191,18 @@ sub purgeCache {
}
#-------------------------------------------------------------------
=head2 view ( $calledAsWebMethod )
Override the base class to implement caching, template and macro processing.
=head3 $calledAsWebMethod
If this is true, then change the cache method, and do not display the
toolbar if in adminMode.
=cut
sub view {
my $self = shift;
my $calledAsWebMethod = shift;
@ -216,15 +228,6 @@ sub view {
return $output;
}
#-------------------------------------------------------------------
sub www_edit {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;
return $self->session->privilege->locked() unless $self->canEditIfLocked;
return $self->getAdminConsole->render($self->getEditForm->print,$self->addEditLabel);
}
#-------------------------------------------------------------------
=head2 www_view
@ -234,12 +237,12 @@ A web accessible version of the view method.
=cut
sub www_view {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canView;
my $mimeType=$self->getValue('mimeType');
$self->session->http->setMimeType($mimeType || 'text/html');
$self->session->http->setCacheControl($self->get("cacheTimeout"));
return $self->view(1);
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canView;
my $mimeType=$self->getValue('mimeType');
$self->session->http->setMimeType($mimeType || 'text/html');
$self->session->http->setCacheControl($self->get("cacheTimeout"));
return $self->view(1);
}

View file

@ -151,7 +151,7 @@ sub getEditForm {
formContent => WebGUI::Form::HTMLArea($session, { name => 'content', richEditId => $wiki->get('richEditor'), value => $self->get('content') }) ,
formSubmit => WebGUI::Form::submit($session, { value => 'Save' }),
formProtect => WebGUI::Form::yesNo($session, { name => "isProtected", value=>$self->getValue("isProtected")}),
formKeywords => WebGUI::Form::text($session, {
formKeywords => WebGUI::Form::keywords($session, {
name => "keywords",
value => WebGUI::Keyword->new($session)->getKeywordsForAsset({asset=>$self}),
}),

View file

@ -0,0 +1,338 @@
package WebGUI::Asset::Wobject::Carousel;
$VERSION = "1.0.0";
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2008 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 JSON;
use Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility;
use base 'WebGUI::Asset::Wobject';
#-------------------------------------------------------------------
=head2 definition ( )
defines wobject properties for New Wobject instances. You absolutely need
this method in your new Wobjects. If you choose to "autoGenerateForms", the
getEditForm method is unnecessary/redundant/useless.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, 'Asset_Carousel');
my %properties;
tie %properties, 'Tie::IxHash';
%properties = (
templateId =>{
fieldType =>"template",
defaultValue =>'CarouselTmpl0000000002',
tab =>"display",
noFormPost =>0,
namespace =>"Carousel",
hoverHelp =>$i18n->get('carousel template description'),
label =>$i18n->get('carousel template label'),
},
items =>{
noFormPost =>1,
fieldType =>'text',
autoGenerate =>0,
},
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
icon=>'Carousel.png',
autoGenerateForms=>1,
tableName=>'Carousel',
className=>'WebGUI::Asset::Wobject::Carousel',
properties=>\%properties
});
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 duplicate ( )
duplicates a New Wobject. This method is unnecessary, but if you have
auxiliary, ancillary, or "collateral" data or files related to your
wobject instances, you will need to duplicate them here.
=cut
sub duplicate {
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
return $newAsset;
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
returns the tabform object that will be used in generating the edit page for New Wobjects.
This method is optional if you set autoGenerateForms=1 in the definition.
=cut
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
my $i18n = WebGUI::International->new($self->session, "Asset_Carousel");
$self->session->style->setScript($self->session->url->extras('yui/build/editor/editor-min.js'), {type =>
'text/javascript'});
$self->session->style->setLink($self->session->url->extras('yui/build/editor/assets/skins/sam/editor.css'), {type
=>'text/css', rel=>'stylesheet'});
$self->session->style->setScript($self->session->url->extras('wobject/Carousel/carousel.js'), {type =>
'text/javascript'});
my $tableRowStart =
'<tr id="items_row">'
.' <td class="formDescription" valign="top" style="width: 180px;"><label for="item1">'
.$i18n->get("items label").'</label><div class="wg-hoverhelp">'.$i18n->get("items description").'</div></td>'
.' <td id="items_td" valign="top" class="tableData">'
.' <input type="button" value="Add item" onClick="javascript:addItem()"></button><br /><br />';
$tabform->getTab("properties")->raw($tableRowStart);
if($self->getValue('items')){
my @items = @{JSON->new->decode($self->getValue('items'))->{items}};
foreach my $item (@items){
my $itemHTML = $i18n->get("id label").'<div class="wg-hoverhelp">'.$i18n->get("id description").'</div>: '
.'<input type="text" id="itemId'.$item->{sequenceNumber}.'" '
.'name="itemId_'.$item->{sequenceNumber}.'" value="'.$item->{itemId}.'">'
.'<textarea id="item'.$item->{sequenceNumber}.'" name="item_'.$item->{sequenceNumber}.'" '
.'class="carouselItemText" rows="#" cols="#" '
.'style="width: 500px; height: 80px;">'.$item->{text}."</textarea><br />\n";
$itemHTML .=
" <script type='text/javascript'>\n"
.'var myEditor'.$item->{sequenceNumber}.' '
.'= new YAHOO.widget.SimpleEditor("item'.$item->{sequenceNumber}.'", '
."{height: '80px', width: '500px', handleSubmit: true});\n"
.'myEditor'.$item->{sequenceNumber}.".render()\n"
."</script>\n";
$tabform->getTab("properties")->raw($itemHTML);
}
}
else{
my $itemHTML = 'ID: <input type="text" id="itemId1" name="itemId_1" value="carousel_item_1">'
.'<textarea id="item1" name="item_1" class="carouselItemText" rows="#" cols="#" '
."style='width: 500px; height: 80px;'></textarea><br />\n";
$itemHTML .=
"<script type='text/javascript'>\n"
."var myEditor1 = new YAHOO.widget.SimpleEditor('item1', {height: '80px', width: '500px', handleSubmit: true});\n"
."myEditor1.render()\n"
."</script>\n";
$tabform->getTab("properties")->raw($itemHTML);
}
my $tableRowEnd = qq|
</td>
</tr>
|;
$tabform->getTab("properties")->raw($tableRowEnd);
return $tabform;
}
#-------------------------------------------------------------------
=head2 prepareView ( )
See WebGUI::Asset::prepareView() for details.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
#$self->session->errorHandler->warn('templateId: '.$self->get("parentId"));
my $template = WebGUI::Asset::Template->new($self->session, $self->get("templateId"));
$template->prepare;
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 processPropertiesFromFormPost ( )
Used to process properties from the form posted.
=cut
sub processPropertiesFromFormPost {
my $self = shift;
my $form = $self->session->form;
my (@items,$items);
$self->SUPER::processPropertiesFromFormPost(@_);
foreach my $param ($form->param) {
if ($param =~ m/^item_/){
my $sequenceNumber = $param;
$sequenceNumber =~ s/^item_//;
if($form->process('itemId_'.$sequenceNumber)){
push(@items,{
sequenceNumber => $sequenceNumber,
text => $form->process($param),
itemId => $form->process('itemId_'.$sequenceNumber),
});
}
}
}
my @sortedItems = sort { $a->{sequenceNumber} cmp $b->{sequenceNumber} } @items;
@items = ();
for (my $i=0; $i<scalar @sortedItems; $i++) {
$sortedItems[$i]->{sequenceNumber} = $i + 1;
push(@items,$sortedItems[$i]);
}
$items = JSON->new->encode({items => \@items});
$self->update({items => $items});
return undef;
}
#-------------------------------------------------------------------
=head2 purge ( )
removes collateral data associated with a Carousel when the system
purges it's data. This method is unnecessary, but if you have
auxiliary, ancillary, or "collateral" data or files related to your
wobject instances, you will need to purge them here.
=cut
sub purge {
my $self = shift;
#purge your wobject-specific data here. This does not include fields
# you create for your Carousel asset/wobject table.
return $self->SUPER::purge;
}
#-------------------------------------------------------------------
=head2 view ( )
method called by the www_view method. Returns a processed template
to be displayed within the page style.
=cut
sub view {
my $self = shift;
my $session = $self->session;
my (@item_loop);
#This automatically creates template variables for all of your wobject's properties.
my $var = $self->get;
if($self->getValue('items')){
$var->{item_loop} = JSON->new->decode($self->getValue('items'))->{items};
}
#This is an example of debugging code to help you diagnose problems.
#WebGUI::ErrorHandler::warn($self->get("templateId"));
return $self->processTemplate($var, undef, $self->{_viewTemplate});
}
#-------------------------------------------------------------------
=head2 www_edit ( )
Web facing method which is the default edit page. This method is entirely
optional. Take it out unless you specifically want to set a submenu in your
adminConsole views.
=cut
#sub www_edit {
# my $self = shift;
# return $self->session->privilege->insufficient() unless $self->canEdit;
# return $self->session->privilege->locked() unless $self->canEditIfLocked;
# my $i18n = WebGUI::International->new($self->session, "Asset_Carousel");
# return $self->getAdminConsole->render($self->getEditForm->print, $i18n->get("edit title"));
#}
#-------------------------------------------------------------------
# Everything below here is to make it easier to install your custom
# wobject, but has nothing to do with wobjects in general
#-------------------------------------------------------------------
# cd /data/WebGUI/lib
# perl -MWebGUI::Asset::Wobject::Carousel -e install www.example.com.conf [ /path/to/WebGUI ]
# - or -
# perl -MWebGUI::Asset::Wobject::Carousel -e uninstall www.example.com.conf [ /path/to/WebGUI ]
#-------------------------------------------------------------------
use base 'Exporter';
our @EXPORT = qw(install uninstall);
use WebGUI::Session;
#-------------------------------------------------------------------
sub install {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::Wobject::Carousel -e install www.example.com.conf\n" unless ($home && $config);
print "Installing asset.\n";
my $session = WebGUI::Session->open($home, $config);
my $assets = $session->config->get( "assets" );
$assets->{ "WebGUI::Asset::Wobject::Carousel" } = { category => "utilities" };
$session->config->set( "assets", $assets );
#$session->config->addToArray("assets","WebGUI::Asset::Wobject::Carousel");
$session->db->write("create table Carousel (
assetId char(22) binary not null,
revisionDate bigint not null,
items mediumtext,
templateId char(22),
primary key (assetId, revisionDate)
)");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
#-------------------------------------------------------------------
sub uninstall {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::Wobject::Carousel -e uninstall www.example.com.conf\n" unless ($home && $config);
print "Uninstalling asset.\n";
my $session = WebGUI::Session->open($home, $config);
$session->config->deleteFromArray("assets","WebGUI::Asset::Wobject::Carousel");
my $rs = $session->db->read("select assetId from asset where className='WebGUI::Asset::Wobject::Carousel'");
while (my ($id) = $rs->array) {
my $asset = WebGUI::Asset->new($session, $id, "WebGUI::Asset::Wobject::Carousel");
$asset->purge if defined $asset;
}
$session->db->write("drop table Carousel");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
1;
#vim:ft=perl

View file

@ -20,9 +20,9 @@ use WebGUI::Paginator;
use WebGUI::Utility;
use WebGUI::Asset::Wobject;
use WebGUI::Workflow::Cron;
use WebGUI::Asset::RSSCapable;
use base 'WebGUI::Asset::RSSCapable';
use base 'WebGUI::Asset::Wobject';
use Class::C3;
use base qw(WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject);
#-------------------------------------------------------------------
sub _computePostCount {
@ -62,27 +62,16 @@ sub _visitorCacheOk {
&& !$self->session->form->process('sortBy'));
}
#-------------------------------------------------------------------
# encode a string to include in xml (for RSS export)
sub _xml_encode {
my $text = shift;
$text =~ s/&/&amp;/g;
$text =~ s/</&lt;/g;
$text =~ s/\]\]>/\]\]&gt;/g;
return $text;
}
#-------------------------------------------------------------------
sub addChild {
my $self = shift;
my $properties = shift;
my @other = @_;
if ($properties->{className} ne "WebGUI::Asset::Post::Thread"
and $properties->{className} ne 'WebGUI::Asset::RSSFromParent') {
if ($properties->{className} ne "WebGUI::Asset::Post::Thread") {
$self->session->errorHandler->security("add a ".$properties->{className}." to a ".$self->get("className"));
return undef;
}
return $self->SUPER::addChild($properties, @other);
return $self->next::method($properties, @other);
}
@ -263,7 +252,7 @@ sub canEdit {
) &&
$self->canStartThread( $userId )
) || # account for new threads
$self->SUPER::canEdit( $userId )
$self->next::method( $userId )
);
}
@ -271,7 +260,7 @@ sub canEdit {
sub canModerate {
my $self = shift;
my $userId = shift || $self->session->user->userId;
return $self->SUPER::canEdit( $userId );
return $self->WebGUI::Asset::Wobject::canEdit( $userId );
}
#-------------------------------------------------------------------
@ -294,7 +283,7 @@ sub canPost {
}
# Users who can edit the collab can post
else {
return $self->SUPER::canEdit( $userId );
return $self->WebGUI::Asset::Wobject::canEdit( $userId );
}
}
@ -322,7 +311,7 @@ sub canStartThread {
;
return (
$user->isInGroup($self->get("canStartThreadGroupId"))
|| $self->SUPER::canEdit( $userId )
|| $self->WebGUI::Asset::Wobject::canEdit( $userId )
);
}
@ -331,13 +320,13 @@ sub canStartThread {
sub canView {
my $self = shift;
my $userId = shift || $self->session->user->userId;
return $self->SUPER::canView( $userId ) || $self->canPost( $userId );
return $self->next::method( $userId ) || $self->canPost( $userId );
}
#-------------------------------------------------------------------
sub commit {
my $self = shift;
$self->SUPER::commit;
$self->next::method;
my $cron = undef;
if ($self->get("getMailCronId")) {
$cron = WebGUI::Workflow::Cron->new($self->session, $self->get("getMailCronId"));
@ -799,13 +788,13 @@ sub definition {
className=>'WebGUI::Asset::Wobject::Collaboration',
properties=>\%properties,
});
return $class->SUPER::definition($session, $definition);
return $class->next::method($session, $definition);
}
#-------------------------------------------------------------------
sub duplicate {
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
my $newAsset = $self->next::method(@_);
$newAsset->createSubscriptionGroup;
return $newAsset;
}
@ -821,7 +810,7 @@ Add a tab for the mail interface.
sub getEditTabs {
my $self = shift;
my $i18n = WebGUI::International->new($self->session,"Asset_Collaboration");
return ($self->SUPER::getEditTabs(), ['mail', $i18n->get('mail'), 9]);
return ($self->next::method, ['mail', $i18n->get('mail'), 9]);
}
#-------------------------------------------------------------------
@ -838,7 +827,7 @@ sub getNewThreadUrl {
}
#-------------------------------------------------------------------
sub getRssItems {
sub getRssFeedItems {
my $self = shift;
# XXX copied and reformatted this query from www_viewRSS, but why is it constructed like this?
@ -861,6 +850,7 @@ SQL
my $datetime = $self->session->datetime;
my @posts;
my $rssLimit = $self->get('itemsPerFeed');
for my $postId (@postIds) {
my $post = WebGUI::Asset->new($self->session, $postId, 'WebGUI::Asset::Post::Thread');
my $postUrl = $siteUrl . $post->getUrl;
@ -881,24 +871,26 @@ SQL
}
}
push @posts, {
push @posts, {
author => $post->get('username'),
title => $post->get('title'),
'link' => $postUrl,
'link' => $postUrl,
guid => $postUrl,
description => $post->get('synopsis'),
epochDate => $post->get('creationDate'),
pubDate => $datetime->epochToMail($post->get('creationDate')),
attachmentLoop => $attachmentLoop,
attachmentLoop => $attachmentLoop,
userDefined1 => $post->get("userDefined1"),
userDefined2 => $post->get("userDefined2"),
userDefined3 => $post->get("userDefined3"),
userDefined4 => $post->get("userDefined4"),
userDefined5 => $post->get("userDefined5"),
};
last if $rssLimit <= scalar(@posts);
}
return @posts;
return \@posts;
}
#-------------------------------------------------------------------
@ -1064,7 +1056,7 @@ sub getViewTemplateVars {
$var{'user.canPost'} = $self->canPost;
$var{'user.canStartThread'} = $self->canStartThread;
$var{"add.url"} = $self->getNewThreadUrl;
$var{"rss.url"} = $self->getRssUrl;
$var{"rss.url"} = $self->getRssFeedUrl;
$var{'user.isModerator'} = $self->canModerate;
$var{'user.isVisitor'} = ($self->session->user->isVisitor);
$var{'user.isSubscribed'} = $self->isSubscribed;
@ -1170,11 +1162,8 @@ See WebGUI::Asset::prepareView() for details.
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
$self->next::method;
my $template = WebGUI::Asset::Template->new($self->session, $self->get("collaborationTemplateId")) or die "no good: ".$self->get("collaborationTemplateId");
if ($self->get('rssCapableRssEnabled')) {
$self->session->style->setLink($self->getRssUrl,{ rel=>'alternate', type=>'application/rss+xml', title=>$self->get('title') . ' RSS' });
}
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
}
@ -1184,7 +1173,7 @@ sub prepareView {
sub processPropertiesFromFormPost {
my $self = shift;
my $updatePrivs = ($self->session->form->process("groupIdView") ne $self->get("groupIdView") || $self->session->form->process("groupIdEdit") ne $self->get("groupIdEdit"));
$self->SUPER::processPropertiesFromFormPost;
$self->next::method;
if ($self->get("subscriptionGroupId") eq "") {
$self->createSubscriptionGroup;
}
@ -1212,7 +1201,7 @@ sub purge {
my $cron = WebGUI::Workflow::Cron->new($self->session, $self->get("getMailCronId"));
$cron->delete if defined $cron;
}
$self->SUPER::purge;
$self->next::method;
}
#-------------------------------------------------------------------
@ -1227,7 +1216,7 @@ sub purgeCache {
my $self = shift;
WebGUI::Cache->new($self->session,"view_".$self->getId)->delete;
WebGUI::Cache->new($self->session,$self->_visitorCacheKey)->delete;
$self->SUPER::purgeCache;
$self->next::method;
}
#-------------------------------------------------------------------
@ -1460,7 +1449,7 @@ sub www_view {
my $self = shift;
my $disableCache = ($self->session->form->process("sortBy") ne "");
$self->session->http->setCacheControl($self->get("visitorCacheTimeout")) if ($self->session->user->isVisitor && !$disableCache);
return $self->SUPER::www_view(@_);
return $self->next::method(@_);
}
1;

View file

@ -359,6 +359,14 @@ sub prepareView {
#------------------------------------------------------------------
=head2 purge ( )
See WebGUI::Asset::purge() for details. Extend SUPERclass
to handle deleting tickets, tokens, ribbons, registrants, badge groups
and event meta data.
=cut
sub purge {
my $self = shift;
my $db = $self->session->db;

View file

@ -206,6 +206,14 @@ sub purgeCache {
}
#-------------------------------------------------------------------
=head2 view ( )
See WebGUI::Asset::view for details. Generate template variables and
render the template. Also handles caching.
=cut
sub view {
my $self = shift;

View file

@ -11,7 +11,8 @@ package WebGUI::Asset::Wobject::Gallery;
#-------------------------------------------------------------------
use strict;
use base 'WebGUI::Asset::Wobject';
use Class::C3;
use base qw(WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject);
use JSON;
use Tie::IxHash;
use WebGUI::International;
@ -338,7 +339,7 @@ sub definition {
properties => \%properties,
};
return $class->SUPER::definition($session, $definition);
return $class->next::method($session, $definition);
}
#----------------------------------------------------------------------------
@ -366,7 +367,7 @@ sub addChild {
return undef;
}
return $self->SUPER::addChild( $properties, @_ );
return $self->next::method( $properties, @_ );
}
#----------------------------------------------------------------------------
@ -740,6 +741,41 @@ sub getPreviousAlbumId {
}
}
#-------------------------------------------------------------------
=head2 getRssFeedItems ()
Returns an array reference of hash references. Each hash reference has a title,
description, link, and date field. The date field can be either an epoch date, an RFC 1123
date, or a ISO date in the format of YYYY-MM-DD HH:MM::SS. Optionally specify an
author, and a guid field.
=cut
sub getRssFeedItems {
my $self = shift;
my $p
= $self->getAlbumPaginator( {
perpage => $self->get('itemsPerFeed'),
} );
my $var = [];
for my $assetId ( @{ $p->getPageData } ) {
my $asset = WebGUI::Asset::Wobject::GalleryAlbum->newPending( $self->session, $assetId );
push @{ $var }, {
'link' => $asset->getUrl,
'guid' => $asset->{_properties}->{ 'assetId' },
'title' => $asset->getTitle,
'description' => $asset->{_properties}->{ 'description' },
'date' => $asset->{_properties}->{ 'creationDate' },
'author' => WebGUI::User->new($self->session, $asset->{_properties}->{ 'ownerUserId' })->username
};
}
return $var;
}
#----------------------------------------------------------------------------
=head2 getSearchPaginator ( rules )
@ -934,7 +970,7 @@ See WebGUI::Asset::prepareView() for details.
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
$self->next::method();
if ( $self->get("viewDefault") eq "album" && $self->get("viewAlbumAssetId") && $self->get("viewAlbumAssetId")
ne 'PBasset000000000000001') {
@ -1042,7 +1078,7 @@ sub www_add {
return $self->processStyle($i18n->get("error add uncommitted"));
}
return $self->SUPER::www_add( @_ );
return $self->next::method( @_ );
}
#----------------------------------------------------------------------------

View file

@ -11,7 +11,8 @@ package WebGUI::Asset::Wobject::GalleryAlbum;
#-------------------------------------------------------------------
use strict;
use base 'WebGUI::Asset::Wobject';
use Class::C3;
use base qw(WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject);
use Carp qw( croak );
use File::Find;
use File::Spec;
@ -77,7 +78,7 @@ sub definition {
properties => \%properties,
};
return $class->SUPER::definition($session, $definition);
return $class->next::method($session, $definition);
}
#----------------------------------------------------------------------------
@ -174,7 +175,7 @@ sub addChild {
return undef;
}
return $self->SUPER::addChild( $properties, @_ );
return $self->next::method( $properties, @_ );
}
#----------------------------------------------------------------------------
@ -400,7 +401,7 @@ sub getCurrentRevisionDate {
return $revisionDate;
}
else {
return $class->SUPER::getCurrentRevisionDate( $session, $assetId );
return $class->next::method( $session, $assetId );
}
}
@ -497,6 +498,41 @@ sub getPreviousAlbum {
return $self->{_previousAlbum};
}
#-------------------------------------------------------------------
=head2 getRssFeedItems ()
Returns an array reference of hash references. Each hash reference has a title,
description, link, and date field. The date field can be either an epoch date, an RFC 1123
date, or a ISO date in the format of YYYY-MM-DD HH:MM::SS. Optionally specify an
author, and a guid field.
=cut
sub getRssFeedItems {
my $self = shift;
my $p
= $self->getFilePaginator( {
perpage => $self->get('itemsPerFeed'),
} );
my $var = [];
for my $assetId ( @{ $p->getPageData } ) {
my $asset = WebGUI::Asset::Wobject::GalleryAlbum->newPending( $self->session, $assetId );
push @{ $var }, {
'link' => $asset->getUrl,
'guid' => $asset->{_properties}->{ 'assetId' },
'title' => $asset->getTitle,
'description' => $asset->{_properties}->{ 'description' },
'date' => $asset->{_properties}->{ 'creationDate' },
'author' => WebGUI::User->new($self->session, $asset->{_properties}->{ 'ownerUserId' })->username
};
}
return $var;
}
#----------------------------------------------------------------------------
=head2 getTemplateVars ( )
@ -639,7 +675,7 @@ See WebGUI::Asset::prepareView() for details.
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
$self->next::method();
my $templateId = $self->getParent->get("templateIdViewAlbum");
@ -719,7 +755,7 @@ approval workflow.
sub processPropertiesFromFormPost {
my $self = shift;
my $form = $self->session->form;
my $errors = $self->SUPER::processPropertiesFromFormPost || [];
my $errors = $self->next::method || [];
# Return if error
return $errors if @$errors;
@ -762,7 +798,7 @@ Override update to force isHidden=1 on all albums.
sub update {
my $self = shift;
my $properties = shift;
return $self->SUPER::update({ %{ $properties }, isHidden=>1 });
return $self->next::method({ %{ $properties }, isHidden=>1 });
}
#----------------------------------------------------------------------------

View file

@ -155,7 +155,7 @@ sub definition {
lineage => $i18n->get('sort by asset rank label'),
lastUpdated => $i18n->get('sort by last updated label'),
},
defaultValue =>"score",
defaultValue =>"title",
hoverHelp =>$i18n->get('default sort description'),
label =>$i18n->get('default sort label'),
},
@ -238,6 +238,14 @@ sub definition {
hoverHelp =>$i18n->get('ratings duration description'),
label =>$i18n->get('ratings duration label'),
},
statisticsCacheTimeout => {
tab => "display",
fieldType => "interval",
defaultValue => 3600,
uiLevel => 8,
label => $i18n->get("statistics cache timeout label"),
hoverHelp => $i18n->get("statistics cache timeout description")
},
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
@ -520,6 +528,7 @@ sub view {
$self->session->style->setScript($self->session->url->extras('wobject/Matrix/matrix.js'), {type =>
'text/javascript'});
my ($varStatistics,$varStatisticsEncoded);
my $var = $self->get;
$var->{isLoggedIn} = ($self->session->user->userId ne "1");
$var->{addMatrixListing_url} = $self->getUrl('func=add;class=WebGUI::Asset::MatrixListing');
@ -527,90 +536,104 @@ sub view {
$var->{exportAttributes_url} = $self->getUrl('func=exportAttributes');
$var->{listAttributes_url} = $self->getUrl('func=listAttributes');
$var->{search_url} = $self->getUrl('func=search');
# Get the MatrixListing with the most views as an object using getLineage.
my ($bestViews_listing) = @{ $self->getLineage(['descendants'], {
if ($self->canEdit){
# Get all the MatrixListings that are still pending.
my @pendingListings = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
orderByClause => "revisionDate asc",
returnObjects => 1,
statusToInclude => ['pending'],
}) };
foreach my $pendingListing (@pendingListings){
push (@{ $var->{pending_loop} }, {
url => $pendingListing->getUrl
."?func=view;revision=".$pendingListing->get('revisionDate'),
name => $pendingListing->get('title'),
});
}
}
my $versionTag = WebGUI::VersionTag->getWorking($session, 1);
my $noCache =
$session->var->isAdminOn
|| $self->get("statisticsCacheTimeout") <= 10
|| ($versionTag && $versionTag->getId eq $self->get("tagId"));
unless ($noCache) {
$varStatisticsEncoded = WebGUI::Cache->new($session,"matrixStatistics_".$self->getId)->get;
}
if ($varStatisticsEncoded){
$varStatistics = JSON->new->decode($varStatisticsEncoded);
}
else{
# Get the MatrixListing with the most views as an object using getLineage.
my ($bestViews_listing) = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
joinClass => "WebGUI::Asset::MatrixListing",
orderByClause => "views desc",
limit => 1,
returnObjects => 1,
}) };
if($bestViews_listing){
$var->{bestViews_url} = $bestViews_listing->getUrl;
$var->{bestViews_count} = $bestViews_listing->get('views');
$var->{bestViews_name} = $bestViews_listing->get('title');
$var->{bestViews_sortButton} = "<span id='sortByViews'><button type='button'>Sort by views</button></span><br />";
}
if($bestViews_listing){
$varStatistics->{bestViews_url} = $bestViews_listing->getUrl;
$varStatistics->{bestViews_count} = $bestViews_listing->get('views');
$varStatistics->{bestViews_name} = $bestViews_listing->get('title');
$varStatistics->{bestViews_sortButton} = "<span id='sortByViews'><button type='button'>Sort by views</button></span><br />";
}
# Get the MatrixListing with the most compares as an object using getLineage.
# Get the MatrixListing with the most compares as an object using getLineage.
my ($bestCompares_listing) = @{ $self->getLineage(['descendants'], {
my ($bestCompares_listing) = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
joinClass => "WebGUI::Asset::MatrixListing",
orderByClause => "compares desc",
limit => 1,
returnObjects => 1,
}) };
if($bestCompares_listing){
$var->{bestCompares_url} = $bestCompares_listing->getUrl;
$var->{bestCompares_count} = $bestCompares_listing->get('compares');
$var->{bestCompares_name} = $bestCompares_listing->get('title');
$var->{bestCompares_sortButton} = "<span id='sortByCompares'><button type='button'>Sort by compares</button></span><br />";
}
if($bestCompares_listing){
$varStatistics->{bestCompares_url} = $bestCompares_listing->getUrl;
$varStatistics->{bestCompares_count} = $bestCompares_listing->get('compares');
$varStatistics->{bestCompares_name} = $bestCompares_listing->get('title');
$varStatistics->{bestCompares_sortButton} = "<span id='sortByCompares'><button type='button'>Sort by compares</button></span><br />";
}
# Get the MatrixListing with the most clicks as an object using getLineage.
my ($bestClicks_listing) = @{ $self->getLineage(['descendants'], {
# Get the MatrixListing with the most clicks as an object using getLineage.
my ($bestClicks_listing) = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
joinClass => "WebGUI::Asset::MatrixListing",
orderByClause => "clicks desc",
limit => 1,
returnObjects => 1,
}) };
if($bestClicks_listing){
$var->{bestClicks_url} = $bestClicks_listing->getUrl;
$var->{bestClicks_count} = $bestClicks_listing->get('clicks');
$var->{bestClicks_name} = $bestClicks_listing->get('title');
$var->{bestClicks_sortButton} = "<span id='sortByClicks'><button type='button'>Sort by clicks</button></span><br />";
}
# Get the 5 MatrixListings that were last updated as objects using getLineage.
if($bestClicks_listing){
$varStatistics->{bestClicks_url} = $bestClicks_listing->getUrl;
$varStatistics->{bestClicks_count} = $bestClicks_listing->get('clicks');
$varStatistics->{bestClicks_name} = $bestClicks_listing->get('title');
$varStatistics->{bestClicks_sortButton} = "<span id='sortByClicks'><button type='button'>Sort by clicks</button></span><br />";
}
my @lastUpdatedListings = @{ $self->getLineage(['descendants'], {
# Get the 5 MatrixListings that were last updated as objects using getLineage.
my @lastUpdatedListings = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
joinClass => "WebGUI::Asset::MatrixListing",
orderByClause => "lastUpdated desc",
limit => 5,
returnObjects => 1,
}) };
foreach my $lastUpdatedListing (@lastUpdatedListings){
push (@{ $var->{last_updated_loop} }, {
foreach my $lastUpdatedListing (@lastUpdatedListings){
push (@{ $varStatistics->{last_updated_loop} }, {
url => $lastUpdatedListing->getUrl,
name => $lastUpdatedListing->get('title'),
lastUpdated => $self->session->datetime->epochToHuman($lastUpdatedListing->get('lastUpdated'),"%z")
});
}
$var->{lastUpdated_sortButton} = "<span id='sortByUpdated'><button type='button'>Sort by updated</button></span><br />";
}
$var->{lastUpdated_sortButton} = "<span id='sortByUpdated'><button type='button'>Sort by updated</button></span><br />";
# For each category, get the MatrixListings with the best ratings.
# Get all the MatrixListings that are still pending.
my @pendingListings = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
orderByClause => "revisionDate asc",
returnObjects => 1,
statusToInclude => ['pending'],
}) };
foreach my $pendingListing (@pendingListings){
push (@{ $var->{pending_loop} }, {
url => $pendingListing->getUrl
."?func=view;revision=".$pendingListing->get('revisionDate'),
name => $pendingListing->get('title'),
});
}
# For each category, get the MatrixListings with the best ratings.
foreach my $category (keys %{$self->getCategories}) {
foreach my $category (keys %{$self->getCategories}) {
my $data;
my $sql = "
select
@ -641,7 +664,7 @@ sub view {
order by rating.meanValue ";
$data = $db->quickHashRef($sql." desc limit 1",[$category,$self->getId]);
push(@{ $var->{best_rating_loop} },{
push(@{ $varStatistics->{best_rating_loop} },{
url=>'/'.$data->{url},
category=>$category,
name=>$data->{productName},
@ -650,7 +673,7 @@ sub view {
count=>$data->{countValue}
});
$data = $db->quickHashRef($sql." asc limit 1",[$category,$self->getId]);
push(@{ $var->{worst_rating_loop} },{
push(@{ $varStatistics->{worst_rating_loop} },{
url=>'/'.$data->{url},
category=>$category,
name=>$data->{productName},
@ -658,9 +681,9 @@ sub view {
median=>$data->{medianValue},
count=>$data->{countValue}
});
}
}
$var->{listingCount} = scalar $db->buildArray("
$varStatistics->{listingCount} = scalar $db->buildArray("
select *
from asset, assetData
where asset.assetId=assetData.assetId
@ -670,7 +693,17 @@ sub view {
and assetData.status='approved'
group by asset.assetId",
[$self->getId]);
$varStatisticsEncoded = JSON->new->encode($varStatistics);
WebGUI::Cache->new($session,"matrixStatistics_".$self->getId)->set(
$varStatisticsEncoded,$self->get("statisticsCacheTimeout")
);
}
foreach my $statistic (keys %{$varStatistics}) {
$var->{$statistic} = $varStatistics->{$statistic};
}
return $self->processTemplate($var, undef, $self->{_viewTemplate});
}
@ -977,13 +1010,32 @@ sub www_getCompareFormData {
my $form = $session->form;
my $sort = shift || $session->scratch->get('matrixSort') || $self->get('defaultSort');
my $sortDirection = ' desc';
# if ( WebGUI::Utility::isIn($sort, qw(revisionDate score)) ) {
# $sortDirection = " desc";
# }
my @results;
my @listingIds = $self->session->form->checkList("listingId");
if ($sort eq 'title'){
$sortDirection = ' asc';
}
$self->session->http->setMimeType("application/json");
my @listingIds = $session->form->checkList("listingId");
$session->http->setMimeType("application/json");
my (@searchParams,@searchParams_sorted,@searchParamList,$searchParamList);
if($form->process("search")){
foreach my $param ($form->param) {
if($param =~ m/^search_/){
my $parameter;
$parameter->{name} = $param;
$parameter->{value} = $form->process($param);
my $attributeId = $param;
$attributeId =~ s/^search_//;
$attributeId =~ s/_____/-/g;
$parameter->{attributeId} = $attributeId;
push(@searchParamList,'"'.$parameter->{attributeId}.'"');
push(@searchParams,$parameter);
}
}
$searchParamList = join(',',@searchParamList);
@searchParams_sorted = sort { $b->{value} <=> $a->{value} } @searchParams;
}
my $sql = "
select
@ -1005,31 +1057,17 @@ assetData.revisionDate
and assetData.revisionDate = (SELECT max(revisionDate) from assetData where assetId=asset.assetId and status='approved')
and status='approved'
order by ".$sort.$sortDirection;
my $sth = $session->db->read($sql,[$self->getId]);
my @results;
@results = @{ $session->db->buildArrayRefOfHashRefs($sql,[$self->getId]) };
my (@searchParams,@searchParams_sorted);
if($form->process("search")){
foreach my $param ($form->param) {
if($param =~ m/^search_/){
my $parameter;
$parameter->{name} = $param;
$parameter->{value} = $form->process($param);
my $attributeId = $param;
$attributeId =~ s/^search_//;
$attributeId =~ s/_____/-/g;
$parameter->{attributeId} = $attributeId;
push(@searchParams,$parameter);
}
}
}
@searchParams_sorted = sort { $b->{value} <=> $a->{value} } @searchParams;
foreach my $result (@results){
if($form->process("search")){
while (my $result = $sth->hashRef) {
my $matrixListing_attributes = $session->db->buildHashRefOfHashRefs("
select value, fieldType, attributeId from Matrix_attribute
left join MatrixListing_attribute as listing using(attributeId)
where listing.matrixListingId = ? order by value asc",
where listing.matrixListingId = ?
and attributeId IN(".$searchParamList.")",
[$result->{assetId}],'attributeId');
PARAM: foreach my $param (@searchParams_sorted) {
my $fieldType = $matrixListing_attributes->{$param->{attributeId}}->{fieldType};
@ -1046,20 +1084,27 @@ assetData.revisionDate
$result->{checked} = 'checked';
}
}
}
else{
$result->{assetId} =~ s/-/_____/g;
if(WebGUI::Utility::isIn($result->{assetId},@listingIds)){
$result->{checked} = 'checked';
}
}
$result->{assetId} =~ s/-/_____/g;
$result->{url} = $session->url->gateway($result->{url});
push @results, $result;
}
}else{
while (my $result = $sth->hashRef) {
$result->{assetId} =~ s/-/_____/g;
if(WebGUI::Utility::isIn($result->{assetId},@listingIds)){
$result->{checked} = 'checked';
}
$result->{url} = $session->url->gateway($result->{url});
push @results, $result;
}
}
$sth->finish;
my $jsonOutput;
$jsonOutput->{ResultSet} = {Result=>\@results};
my $encodedOutput = JSON->new->encode($jsonOutput);
return $encodedOutput;
}

View file

@ -297,8 +297,8 @@ Returns a toolbar with a set of icons that hyperlink to functions that delete, e
sub getToolbar {
my $self = shift;
return
unless $self->canEdit;
return undef
unless $self->canEdit && $self->session->var->isAdminOn;
if ($self->getToolbarState) {
my $toolbar = '';
if ($self->canEditIfLocked) {

View file

@ -311,7 +311,7 @@ sub getEditForm {
);
# javascript
$self->session->style->setScript("/extras/wobject/SQLReport/editFormDownload.js");
$self->session->style->setScript($self->session->url->extras("wobject/SQLReport/editFormDownload.js"), {type => 'text/javascript',});
### /DOWNLOAD

View file

@ -308,10 +308,11 @@ sub view {
foreach my $id (@{$p->getPageData}) {
my $asset = WebGUI::Asset->newByDynamicClass($session, $id);
if (defined $asset) {
my $sku = $asset->get;
$sku->{url} = $asset->getUrl;
$sku->{thumbnailUrl} = $asset->getThumbnailUrl;
$sku->{price} = sprintf("%.2f", $asset->getPrice);
my $sku = $asset->get;
$sku->{url} = $asset->getUrl;
$sku->{thumbnailUrl} = $asset->getThumbnailUrl;
$sku->{price} = sprintf("%.2f", $asset->getPrice);
$sku->{addToCartForm} = $asset->getAddToCartForm;
push @skus, $sku;
}
else {
@ -330,6 +331,13 @@ sub view {
#-------------------------------------------------------------------
=head2 www_edit ( )
Override the superclass to add import and exprt items to the AdminConsole submenu.
=cut
sub www_edit {
my $self = shift;
my $i18n = WebGUI::International->new($self->session, 'Asset_Shelf');

View file

@ -112,6 +112,14 @@ sub definition {
label => $i18n->get('Max user responses'),
hoverHelp => $i18n->get('Max user responses help'),
},
surveySummaryTemplateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('Survey Summary Template'),
hoverHelp => $i18n->get('Survey Summary Template help'),
defaultValue => '7F-BuEHi7t9bPi008H8xZQ',
namespace => 'Survey/Summary',
},
surveyTakeTemplateId => {
tab => 'display',
fieldType => 'template',
@ -193,8 +201,19 @@ sub definition {
# hoverHelp => $i18n->get('editForm workflowIdAddEntry description'),
none => 1,
},
quizModeSummary => {
fieldType => 'yesNo',
defaultValue => 0,
tab => 'properties',
label => $i18n->get('Quiz mode summaries'),
hoverHelp => $i18n->get('Quiz mode summaries help'),
}
);
#my $defaultMC = $session->
#%properties = ();
push @{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'survey.gif',
@ -412,6 +431,10 @@ sub www_submitObjectEdit {
}
elsif ( $params->{copy} ) {
return $self->copyObject( \@address );
}elsif( $params->{removetype} ){
return $self->removeType(\@address);
}elsif( $params->{addtype} ){
return $self->addType($params->{addtype},\@address);
}
# Update the addressed object
@ -493,6 +516,51 @@ sub www_jumpTo {
#-------------------------------------------------------------------
=head2 removeType ( $address )
Remove the requested questionType, and then reloads the Survey.
=head3 $address
Specifies which questionType to delete.
=cut
sub removeType{
my $self = shift;
my $address = shift;
$self->surveyJSON->removeType($address);
return $self->www_loadSurvey( { address => $address } );
}
#-------------------------------------------------------------------
=head2 addType ( $name, $address )
Adds a new questionType, and then reloads the Survey.
=head3 $name
The name of the new question type.
=head3 $address
Specifies where to add the question.
=cut
sub addType{
my $self = shift;
my $name = shift;
my $address = shift;
$self->surveyJSON->addType($name,$address);
$self->persistSurveyJSON();
return $self->www_loadSurvey( { address => $address } );
}
#-------------------------------------------------------------------
=head2 copyObject ( )
Takes the address of a survey object and creates a copy. The copy is placed at the end of this object's parent's list.
@ -705,8 +773,8 @@ sub www_loadSurvey {
}
# Generate the list of valid goto targets
my @gotoTargets = $self->surveyJSON->getGotoTargets;
my $gotoTargets = $self->surveyJSON->getGotoTargets;
my %buttons;
$buttons{question} = $address->[0];
if ( @{$address} == 2 or @{$address} == 3 ) {
@ -753,7 +821,9 @@ sub www_loadSurvey {
$lastType = 'answer';
}
}
my $warnings = $self->surveyJSON->validateSurvey();
my $return = {
address => $address, # the address of the focused object
buttons => \%buttons, # the data to create the Add buttons
@ -761,7 +831,8 @@ sub www_loadSurvey {
ddhtml => $html, # the html to create the draggable html divs
ids => \@ids, # list of all ids passed in which are draggable (for adding events)
type => $var->{type}, # the object type
gotoTargets => \@gotoTargets,
gotoTargets => $gotoTargets,
warnings => $warnings #List of warnings to display to the user
};
$self->session->http->setMimeType('application/json');
@ -883,8 +954,8 @@ sub getResponseInfoForView {
my ( $code, $taken );
my $maxTakes = $self->getValue('maxResponsesPerUser');
my $id = $self->session->user->userId();
my $maxResponsesPerUser = $self->getValue('maxResponsesPerUser');
my $userId = $self->session->user->userId();
my $anonId
= $self->session->form->process('userid')
|| $self->session->http->getCookies->{Survey2AnonId}
@ -894,45 +965,45 @@ sub getResponseInfoForView {
my $string;
#if there is an anonid or id is for a WG user
if ( $anonId or $id != 1 ) {
if ( $anonId or $userId != 1 ) {
$string = 'userId';
if ($anonId) {
$string = 'anonId';
$id = $anonId;
$userId = $anonId;
}
my $responseId
= $self->session->db->quickScalar(
"select Survey_responseId from Survey_response where $string = ? and assetId = ? and isComplete = 0",
[ $id, $self->getId() ] );
[ $userId, $self->getId() ] );
if ( !$responseId ) {
$code = $self->session->db->quickScalar(
"select isComplete from Survey_response where $string = ? and assetId = ? and isComplete > 0 order by endDate desc limit 1",
[ $id, $self->getId() ]
[ $userId, $self->getId() ]
);
}
$taken
= $self->session->db->quickScalar(
"select count(*) from Survey_response where $string = ? and assetId = ? and isComplete > 0",
[ $id, $self->getId() ] );
[ $userId, $self->getId() ] );
}
elsif ( $id == 1 ) {
elsif ( $userId == 1 ) {
my $responseId = $self->session->db->quickScalar(
'select Survey_responseId from Survey_response where userId = ? and ipAddress = ? and assetId = ? and isComplete = 0',
[ $id, $ip, $self->getId() ]
[ $userId, $ip, $self->getId() ]
);
if ( !$responseId ) {
$code = $self->session->db->quickScalar(
'select isComplete from Survey_response where userId = ? and ipAddress = ? and assetId = ? and isComplete > 0 order by endDate desc limit 1',
[ $id, $ip, $self->getId() ]
[ $userId, $ip, $self->getId() ]
);
}
$taken = $self->session->db->quickScalar(
'select count(*) from Survey_response where userId = ? and ipAddress = ? and assetId = ? and isComplete > 0',
[ $id, $ip, $self->getId() ]
[ $userId, $ip, $self->getId() ]
);
}
return ( $code, $taken >= $maxTakes );
return ( $code, $maxResponsesPerUser > 0 && $taken >= $maxResponsesPerUser );
}
#-------------------------------------------------------------------
@ -1081,6 +1152,14 @@ sub www_submitQuestions {
}
#-------------------------------------------------------------------
sub getSummary{
my $self = shift;
my $summary = $self->responseJSON->showSummary();
my $out = $self->processTemplate( $summary, $self->get('surveySummaryTemplateId') );
return $out;
# return $self->session->style->process( $out, $self->get('styleTemplateId') );
}
#-------------------------------------------------------------------
=head2 www_loadQuestions
@ -1092,7 +1171,7 @@ Determines which questions to display to the survey taker next, loads and return
sub www_loadQuestions {
my $self = shift;
my $wasRestarted = shift;
if ( !$self->canTakeSurvey() ) {
$self->session->log->debug('canTakeSurvey false, surveyEnd');
return $self->surveyEnd();
@ -1110,6 +1189,12 @@ sub www_loadQuestions {
if ( $self->responseJSON->surveyEnd() ) {
$self->session->log->debug('Response surveyEnd, so calling surveyEnd');
if ( $self->get('quizModeSummary') ) {
if(! $self->session->form->param('shownsummary')){
my $json = to_json( { type => 'summary', summary => $self->getSummary() });
return $json;
}
}
return $self->surveyEnd();
}
@ -1191,7 +1276,7 @@ sub surveyEnd {
}
}
}
$url = $self->session->url->gateway($url);
$url = $self->session->url->gateway($url) if($url !~ /^http:/i);
#$self->session->http->setRedirect($url);
#$self->session->http->setMimeType('application/json');
my $json = to_json( { type => 'forward', url => $url } );
@ -1208,17 +1293,18 @@ Sends the processed template and questions structure to the client
sub prepareShowSurveyTemplate {
my ( $self, $section, $questions ) = @_;
my %multipleChoice = (
'Multiple Choice', 1, 'Gender', 1, 'Yes/No', 1, 'True/False', 1, 'Ideology', 1,
'Race', 1, 'Party', 1, 'Education', 1, 'Scale', 1, 'Agree/Disagree', 1,
'Oppose/Support', 1, 'Importance', 1, 'Likelihood', 1, 'Certainty', 1, 'Satisfaction', 1,
'Confidence', 1, 'Effectiveness', 1, 'Concern', 1, 'Risk', 1, 'Threat', 1,
'Security', 1
);
# my %multipleChoice = (
# 'Multiple Choice', 1, 'Gender', 1, 'Yes/No', 1, 'True/False', 1, 'Ideology', 1,
# 'Race', 1, 'Party', 1, 'Education', 1, 'Scale', 1, 'Agree/Disagree', 1,
# 'Oppose/Support', 1, 'Importance', 1, 'Likelihood', 1, 'Certainty', 1, 'Satisfaction', 1,
# 'Confidence', 1, 'Effectiveness', 1, 'Concern', 1, 'Risk', 1, 'Threat', 1,
# 'Security', 1
# );
my %textArea = ( 'TextArea', 1 );
my %text = ( 'Text', 1, 'Email', 1, 'Phone Number', 1, 'Text Date', 1, 'Currency', 1 );
my %slider = ( 'Slider', 1, 'Dual Slider - Range', 1, 'Multi Slider - Allocate', 1 );
my %dateType = ( 'Date', 1, 'Date Range', 1 );
my %dateShort = ( 'Year Month', 1 );
my %fileUpload = ( 'File Upload', 1 );
my %hidden = ( 'Hidden', 1 );
@ -1227,7 +1313,7 @@ sub prepareShowSurveyTemplate {
elsif ( $text{ $q->{questionType} } ) { $q->{textType} = 1; }
elsif ( $textArea{ $q->{questionType} } ) { $q->{textAreaType} = 1; }
elsif ( $hidden{ $q->{questionType} } ) { $q->{hidden} = 1; }
elsif ( $multipleChoice{ $q->{questionType} } ) {
elsif ( $self->surveyJSON->multipleChoiceTypes->{ $q->{questionType} } ) {
$q->{multipleChoice} = 1;
if ( $q->{maxAnswers} > 1 ) {
$q->{maxMoreOne} = 1;
@ -1236,6 +1322,26 @@ sub prepareShowSurveyTemplate {
elsif ( $dateType{ $q->{questionType} } ) {
$q->{dateType} = 1;
}
elsif ( $dateShort{ $q->{questionType} } ) {
$q->{dateShort} = 1;
foreach my $a(@{$q->{answers}}){
$a->{months} = [
{'month' => ''},
{'month' => 'January'},
{'month' => 'February'},
{'month' => 'March'},
{'month' => 'April'},
{'month' => 'May'},
{'month' => 'June'},
{'month' => 'July'},
{'month' => 'August'},
{'month' => 'September'},
{'month' => 'October'},
{'month' => 'November'},
{'month' => 'December'}
];
}
}
elsif ( $slider{ $q->{questionType} } ) {
$q->{slider} = 1;
if ( $q->{questionType} eq 'Dual Slider - Range' ) {
@ -1331,7 +1437,7 @@ sub persistResponseJSON {
#-------------------------------------------------------------------
=head2 responseId
=head2 responseIdCookies
Mutator for the responseIdCookies that determines whether cookies are used as
part of the L<"responseId"> lookup process.
@ -1412,7 +1518,7 @@ sub responseId {
}
if ( !$responseId ) {
my $allowedTakes = $self->get('maxResponsesPerUser');
my $maxResponsesPerUser = $self->get('maxResponsesPerUser');
my $haveTaken;
if ( $id == 1 ) {
@ -1428,7 +1534,7 @@ sub responseId {
[ $id, $self->getId() ] );
}
if ( $haveTaken < $allowedTakes ) {
if ( $maxResponsesPerUser == 0 || $haveTaken < $maxResponsesPerUser ) {
$responseId = $self->session->db->setRow(
'Survey_response',
'Survey_responseId', {
@ -1450,7 +1556,7 @@ sub responseId {
$self->persistResponseJSON();
}
else {
$self->session->log->debug("haveTaken ($haveTaken) >= allowedTakes ($allowedTakes)");
$self->session->log->debug("haveTaken ($haveTaken) >= maxResponsesPerUser ($maxResponsesPerUser)");
}
}
$self->{responseId} = $responseId;
@ -1475,25 +1581,26 @@ sub canTakeSurvey {
return 0;
}
my $maxTakes = $self->getValue('maxResponsesPerUser');
my $ip = $self->session->env->getIp;
my $id = $self->session->user->userId();
my $takenCount = 0;
my $maxResponsesPerUser = $self->getValue('maxResponsesPerUser');
my $ip = $self->session->env->getIp;
my $userId = $self->session->user->userId();
my $takenCount = 0;
if ( $id == 1 ) {
if ( $userId == 1 ) {
$takenCount = $self->session->db->quickScalar(
'select count(*) from Survey_response where userId = ? and ipAddress = ? '
. 'and assetId = ? and isComplete > ?', [ $id, $ip, $self->getId(), 0 ]
. 'and assetId = ? and isComplete > ?', [ $userId, $ip, $self->getId(), 0 ]
);
}
else {
$takenCount
= $self->session->db->quickScalar(
'select count(*) from Survey_response where userId = ? and assetId = ? and isComplete > ?',
[ $id, $self->getId(), 0 ] );
[ $userId, $self->getId(), 0 ] );
}
if ( $takenCount >= $maxTakes ) {
# A maxResponsesPerUser value of 0 implies unlimited
if ( $maxResponsesPerUser > 0 && $takenCount >= $maxResponsesPerUser ) {
$self->{canTake} = 0;
}
else {
@ -1642,6 +1749,13 @@ sub www_viewStatisticalOverview {
}
#-------------------------------------------------------------------
=head2 www_exportTransposedResults ()
Exports transposed results in a tab deliniated file.
=cut
sub www_exportSimpleResults {
my $self = shift;
@ -1660,7 +1774,7 @@ sub www_exportSimpleResults {
#-------------------------------------------------------------------
=head2 www_exportTransposedResults (){
=head2 www_exportTransposedResults ()
Returns transposed results as a tabbed file.

View file

@ -0,0 +1,217 @@
package WebGUI::Asset::Wobject::Survey::ExpressionEngine;
=head1 NAME
Package WebGUI::Asset::Wobject::Survey::ExpressionEngine
=head1 DESCRIPTION
This class is used to process Survey gotoExpressions.
See L<run> for more details.
=cut
use strict;
use Params::Validate qw(:all);
use Safe;
use Data::Dumper;
use List::Util qw/sum/;
Params::Validate::validation_options( on_fail => sub { WebGUI::Error::InvalidParam->throw( error => shift ) } );
# We need these as semi-globals so that utility subs (which are shared with the safe compartment)
# can access them.
my $session;
my $values;
my $scores;
my $jump_count;
my $validate;
my $validTargets;
=head2 value
Utility sub that gives expressions access to recorded response values
value(question_variable) returns the recorded response value for the answer to question_variable
=cut
sub value($) {
my $key = shift;
my $value = $values->{$key};
$session->log->debug("[$key] resolves to [$value]");
return $value; # scalar variable, so no need to clone
}
=head2 score
Utility sub that gives expressions access to recorded response scores.
score(question_variable) returns the score for the answer selected for question_variable
score(section_variable) returns the summed score for the answers to all the questions in section_variable
=cut
sub score($) {
my $key = shift;
my $score = $scores->{$key};
$session->log->debug("[$key] resolves to [$score]");
return $score; # scalar variable, so no need to clone
}
=head2 jump
Utility sub shared with Safe compartment so that expressions can call individual jump tests.
Throws an exception containing the jump target when a jump matches, thus allowing L<run> to
catch the first successful jump.
=cut
sub jump(&$) {
my ( $sub, $target ) = @_;
$jump_count++;
# If $validTargets known, make sure target is valid
if ($validTargets && !exists $validTargets->{$target}) {
$session->log->debug("Invalid target [$target]");
if ($validate) {
die("Invalid jump target \"$target\""); # bail and report error
} else {
return; # skip jump but continue with expression
}
}
if ( $sub->() ) {
$session->log->debug("jump call #$jump_count is truthy");
die( { jump => $target } );
}
else {
$session->log->debug("jump call #$jump_count is falsey");
}
}
=head2 avg
Utility sub shared with Safe compartment to allows expressions to easily compute the average of a list
=cut
sub avg {
my @vals = @_;
return sum(@vals) / @vals;
}
=head2 run ( $session, $expression, $opts )
Class method.
Evaluates the given expression in a Safe compartment.
=head3 session
A WebGUI::Session
=head3 expression
The expression to run.
A gotoExpression is essentially a perl expression that gets evaluated in a Safe compartment.
To access Section/Question recorded response values, the expression calls L<value>.
To access Section/Question recorded response scores, the expression calls L<score>.
To trigger a jump, the expression calls L<jump>. The first truthy jump succeeds.
We also give expressions access to some useful utility subs such as avg(), and all of the
handy subs from List::Util (min, max, sum, etc..).
A very simple expression that checks if the response to s1q1 is 0 might look like:
jump { value(s1q1) == 0 } target
A more complicated gotoExpression with two possible jumps might look like:
jump { value(q1) > 5 and value(s2q1) =~ m/textmatch/ } target1;
jump { avg(value(q1), value(q2), value(q3)) > 10 } target2;
=head3 opts (optional)
Supported options are:
=over 3
=item * values
Hashref of values to make available to the expression via the L<value> utility sub
=item * scores
Hashref of scores to make available to the expression via the L<score> utility sub
=item* validTargets
A hashref of valid jump targets. If this is provided, all L<jump> calls will fail unless
the specified target is a key in the hashref.
=item * validate
Return errors rather than just logging them (useful for displaying survey validation errors to users)
=back
=cut
sub run {
my $class = shift;
my ( $s, $expression, $opts )
= validate_pos( @_, { isa => 'WebGUI::Session' }, { type => SCALAR }, { type => HASHREF, default => {} } );
# Init package globals
( $session, $values, $scores, $jump_count, $validate, $validTargets ) = ( $s, $opts->{values}, $opts->{scores}, 0, $opts->{validate}, $opts->{validTargets} );
if (!$session->config->get('enableSurveyExpressionEngine')) {
$session->log->debug('enableSurveyExpressionEngine config option disabled, skipping');
return;
}
# Create the Safe compartment
my $compartment = Safe->new();
# Share our utility subs with the compartment
$compartment->share('&value');
$compartment->share('&score');
$compartment->share('&jump');
$compartment->share('&avg');
# Give them all of List::Util too
$compartment->share_from('List::Util', ['&first', '&max', '&maxstr', '&min', '&minstr', '&reduce', '&shuffle', '&sum',]);
$session->log->debug("Expression is: \"$expression\"");
$compartment->reval($expression);
# See if we ran the engine just to check for errors
if ($opts->{validate}) {
if ($@ && ref $@ ne 'HASH') {
my $error = $@;
$error =~ s/(.*?) at .*/$1/s; # don't reveal too much
return $error;
}
return; # no validation errors
}
# A successful jump triggers a hashref containing the jump target to be thrown
if ( ref $@ && ref $@ eq 'HASH' && $@->{jump} ) {
my $jump = $@->{jump};
$session->log->debug("Returning [$jump]");
return $jump;
}
# Log all other errors (for example compile errors from bad expressions)
if ($@) {
$session->log->error($@);
}
# Return undef on failure
return;
}
1;

View file

@ -28,7 +28,7 @@ As a whole, this class represents the complete state of a user's response to a S
At the heart of this class is a perl hash that can be serialized
as JSON to the database to allow for storage and retrieval of the complete state
of a survey response.
Survey instances that allow users to record multiple responses will persist multiple
instances of this class to the database (one per distinct user response).
@ -40,7 +40,7 @@ number of questions answered (L<"questionsAnswered">) and the Survey start time
This package is not intended to be used by any other Asset in WebGUI.
=head2 surveyOrder
This data strucutre is an array (reference) of Survey addresses (see
L<WebGUI::Asset::Wobject::Survey::SurveyJSON/Address Parameter>), stored in the order
in which items are presented to the user.
@ -70,7 +70,7 @@ is stored in this hash reference.
Questions keys are constructed by hypenating the relevant L<"sIndex"> and L<"qIndex">.
Answer keys are constructed by hypenating the relevant L<"sIndex">, L<"qIndex"> and L<aIndex|"aIndexes">.
Question entries only contain a comment field:
{
...
@ -79,7 +79,7 @@ Question entries only contain a comment field:
}
...
}
Answers entries contain: value (the recorded value), time and comment fields.
{
@ -98,6 +98,7 @@ use strict;
use JSON;
use Params::Validate qw(:all);
use List::Util qw(shuffle);
use Safe;
Params::Validate::validation_options( on_fail => sub { WebGUI::Error::InvalidParam->throw( error => shift ) } );
#-------------------------------------------------------------------
@ -430,9 +431,9 @@ Processes and records submitted survey responses in the L<"responses"> data stru
Does terminal handling, and branch processing, and advances the L<"lastResponse"> index
if all required questions have been answered.
=head3 $responses
=head3 $submittedResponses
A hash ref of form param data. Each element should look like:
A hash ref of submitted form param data. Each element should look like:
{
"questionId-comment" => "question comment",
@ -459,11 +460,11 @@ gotoExpression in the set of questions wins.
sub recordResponses {
my $self = shift;
my ($responses) = validate_pos( @_, { type => HASHREF } );
my ($submittedResponses) = validate_pos( @_, { type => HASHREF } );
# Build a lookup table of non-multiple choice question types
my %knownTypes = map {$_ => 1} $self->survey->specialQuestionTypes;
my %knownTypes = map {$_ => 1} @{$self->survey->specialQuestionTypes};
# We want to record responses against the "next" response section and questions, since these are
# the items that have just been displayed to the user.
my $section = $self->nextResponseSection();
@ -517,37 +518,40 @@ sub recordResponses {
}
# Record Question comment
$self->responses->{ $question->{id} }->{comment} = $responses->{ $question->{id} . 'comment' };
$self->responses->{ $question->{id} }->{comment} = $submittedResponses->{ $question->{id} . 'comment' };
# Process Answers in Question..
for my $answer ( @{ $question->{answers} } ) {
# Pluck the values out of the responses hash that we want to record..
my $answerValue = $responses->{ $answer->{id} };
my $answerComment = $responses->{ $answer->{id} . 'comment' };
my $submittedAnswerResponse = $submittedResponses->{ $answer->{id} };
my $submittedAnswerComment = $submittedResponses->{ $answer->{id} . 'comment' };
# Proceed if we're satisfied that response is valid..
if ( defined $answerValue && $answerValue =~ /\S/ ) {
# Proceed if we're satisfied that the submitted answer response is valid..
if ( defined $submittedAnswerResponse && $submittedAnswerResponse =~ /\S/ ) {
$aAnswered = 1;
if ($knownTypes{$question->{questionType}}) {
$self->responses->{ $answer->{id} }->{value} = $answerValue;
} else {
# Unknown type, must be a multi-choice bundle
# For Multi-choice, use recordedAnswer instead of answerValue
$self->responses->{ $answer->{id} }->{value} = $answer->{recordedAnswer};
}
$self->responses->{ $answer->{id} }->{time} = time;
$self->responses->{ $answer->{id} }->{comment} = $answerComment;
# Now, decide what to record. For multi-choice questions, use recordedAnswer.
# Otherwise, we use the (raw) submitted response (e.g. text input, date input etc..)
$self->responses->{ $answer->{id} }->{value}
= $knownTypes{ $question->{questionType} }
? $submittedAnswerResponse
: $answer->{recordedAnswer};
$self->responses->{ $answer->{id} }->{time} = time;
$self->responses->{ $answer->{id} }->{comment} = $submittedAnswerComment;
# Handle terminal Answers..
if ( $answer->{terminal} ) {
$terminal = 1;
$terminalUrl = $answer->{terminalUrl};
}
# ..and also gotos..
elsif ( $answer->{goto} =~ /\w/ ) {
$goto = $answer->{goto};
}
# .. and also gotoExpressions..
elsif ( $answer->{gotoExpression} =~ /\w/ ) {
$gotoExpression = $answer->{gotoExpression};
@ -645,89 +649,26 @@ indicates that we should branch.
=head3 $gotoExpression
The gotoExpression.
A gotoExpression is a string representing a list of expressions (one per line) of the form:
target: expression
target: expression
...
This subroutine iterates through the list, processing each line and, all things being
well, evaluates the expression. The first expression to evaluate to true triggers a
call to goto($target).
The expression is a simple subset of the formula language used in spreadsheet programs
such as Excel, OpenOffice, Google Docs etc..
Here is an example using section variables S1 and S2 as jump targets and question
variables Q1-3 in the expression. It jumps to S1 if the user's answer to Q1 has a value
of 3, jumps to S2 if Q2 + Q3 < 10, and otherwise doesn't branch at all (the default).
S1: Q1 = 3
S2: Q2 + Q3 < 10
Arguments are evaluated as follows:
Numeric arguments evaluate as numbers
=over 4
=item * No support for strings (and hence no string matching)
=item * Question variable names (e.g. Q1) evaluate to the numeric value associated with
user's answer to that question, or undefined if the user has not answered that question
=back
Binary comparisons operators: = != < <= >= >
=over 4
=item * return boolean values based on perl's equivalent numeric comparison operators
=back
Simple math operators: + - * /
=over 4
=item * return numeric values
=back
Later we may add Boolean operators: AND( x; y; z; ... ), OR( x; y; z; ... ), NOT( x ), with args separated by
semicolons (presumably because spreadsheet formulas use commas to indicate cell ranges)
Later still you may be able to say AVG(section1) or SUM(section3) and have those functions automatically
compute their result over the set of all questions in the given section.
But for now those things can be done manually using the limited subset defined.
The gotoExpression. See L<WebGUI::Asset::Wobject::Survey::ExpressionEngine> for more info.
=cut
sub processGotoExpression {
my $self = shift;
my ($expression) = validate_pos(@_, {type => SCALAR});
my $responses = $self->recordedResponses();
# Parse gotoExpressions one after the other (first one that's true wins)
foreach my $line (split /\n/, $expression) {
my $processed = $self->parseGotoExpression($line, $responses);
next if !$processed;
# (ab)use perl's eval to evaluate the processed expression
my $result = eval "$processed->{expression}"; ## no critic
$self->session->log->warn($@) if $@; ## no critic
if ($result) {
$self->session->log->debug("Truthy, goto [$processed->{target}]");
$self->processGoto($processed->{target});
return $processed;
} else {
$self->session->log->debug('Falsy, not branching');
next;
}
# Prepare the ingredients..
my $values = $self->responseValuesByVariableName;
my $scores = $self->responseScoresByVariableName;
my %validTargets = map { $_ => 1 } @{$self->survey->getGotoTargets};
use WebGUI::Asset::Wobject::Survey::ExpressionEngine;
my $engine = "WebGUI::Asset::Wobject::Survey::ExpressionEngine";
if (my $jump = $engine->run($self->session, $expression, { values => $values, scores => $scores, validTargets => \%validTargets} )) {
$self->session->log->debug("Hit. Jumping to [$jump]");
$self->processGoto($jump);
}
$self->session->log->debug("No hits, falling through");
return;
}
@ -735,111 +676,129 @@ sub processGotoExpression {
=head2 recordedResponses
Returns a hash (reference) of question responses. The hash keys are
question variable names. The hash values are the corresponding answer
values selected by the user.
Returns an array or response information in this response's survey order.
=cut
sub recordedResponses {
sub recordedResponses{
my $self = shift;
my $responses= {
# questionName => response answer value
};
# Populate %responses with the user's data..
my $responses= [
# {answer info hash}
];
# Populate @$responses with the user's data..
for my $address ( @{ $self->surveyOrder } ) {
my $question = $self->survey->question( $address );
my ($sIndex, $qIndex) = (sIndex($address), qIndex($address));
for my $aIndex (aIndexes($address)) {
my $question = $self->survey->question([$sIndex,$qIndex]);
my $answerId = $self->answerId($sIndex, $qIndex, $aIndex);
if ( defined $self->responses->{$answerId} ) {
my $answer = $self->survey->answer( [ $sIndex, $qIndex, $aIndex ] );
$responses->{$question->{variable}}
= $answer->{value} =~ /\w/ ? $answer->{value}
: $question->{value}
;
push(@$responses, {
value => $answer->{value} =~ /\w/ ? $answer->{value} : $question->{value},
recordedAnswer => $answer->{recordedAnswer},
isCorrect => $answer->{isCorrect},
answerText => $answer->{text},
address => [$sIndex,$qIndex,$aIndex],
questionText => $question->{text},
questionValue => $question->{value},
questionType => $question->{questionType}
}
);
}
}
}
return $responses;
}
#-------------------------------------------------------------------
=head2 parseGotoExpression( ( $expression, $responses)
=head2 responseValuesByVariableName
Parses a single gotoExpression. Returns undef if processing fails, or the following hashref
if things work out well:
{ target => $target, expression => $expression }
Returns a lookup table to question variable names and recorded response values.
=head3 $expression
The expression to process
=head3 $responses
Hashref that maps questionNames to response values
=head3 Explanation:
Uses the following simple strategy:
First, parse the expression as:
target: expression
Replace each questionName with its response value (from the $responses hashref)
Massage the expression into valid perl
Check that only valid tokens remain. This last step ensures that any invalid questionNames in
the expression generate an error because our list of valid tokens doesn't include a-z
Only questions with a defined variable name set are included. Values come from
the L<responses> hash.
=cut
sub parseGotoExpression {
my $self = shift;
my ($expression, $responses) = validate_pos(@_, { type => SCALAR }, { type => HASHREF, default => {} });
$self->session->log->debug("Parsing gotoExpression: $expression");
# Valid gotoExpression tokens are..
my $tokens = qr{\s|[-0-9=!<>+*/.()]};
my ( $target, $rest ) = $expression =~ /\s* ([^:]+?) \s* : \s* (.*)/x;
$self->session->log->debug("Parsed as Target: [$target], Expression: [$rest]");
if ( !defined $target ) {
$self->session->log->warn('Target undefined');
return;
sub responseValuesByVariableName {
my $self = shift;
my %lookup;
while (my ($address, $response) = each %{$self->responses}) {
next if (!$response || !$address);
# Turn responses s-q-a string into an address array
my @address = split /-/, $address;
# Filter out the non-answer entries
next unless @address == 3;
# Grab the corresponding question
my $question = $self->survey->question([@address]);
# Filter out questions without defined variable names
next if !$question || !defined $question->{variable};
# Add variable => value to our hash
$lookup{$question->{variable}} = $response->{value};
}
return \%lookup;
}
if ( !defined $rest || $rest eq q{} ) {
$self->session->log->warn('Expression undefined');
return;
#-------------------------------------------------------------------
=head2 responseScoresByVariableName
Returns a lookup table to question variable names and recorded response values.
Only questions with a defined variable name set are included. Scores come from
the L<responses> hash.
=cut
sub responseScoresByVariableName {
my $self = shift;
my %lookup;
while (my ($address, $response) = each %{$self->responses}) {
next if (!$response || !$address);
# Turn responses s-q-a string into an address array
my @address = split /-/, $address;
# Filter out the non-answer entries
next unless @address == 3;
# Grab the corresponding question
my $question = $self->survey->question([@address]);
# Filter out questions without defined variable names
next if !$question || !defined $question->{variable};
# Grab the corresponding answer
my $answer = $self->survey->answer([@address]);
# Add variable => score to our hash
$lookup{$question->{variable}} = $answer->{value};
}
# Replace each questionName with its response value
while ( my ( $questionName, $response ) = each %{$responses} ) {
$rest =~ s/$questionName/$response/g;
# Add section score totals
for my $s (@{$self->survey->sections}) {
next unless $s->{variable};
my $score = 0;
for my $q (@{$s->{questions}}) {
next unless $q->{variable};
next unless exists $lookup{$q->{variable}};
$lookup{$s->{variable}} += $lookup{$q->{variable}};
}
}
# convert '=' to '==' but don't touch '!=', '<=' or '>='
$rest =~ s/(?<![!<>])=(?!=)/==/g;
if ( $rest !~ /^$tokens+$/ ) {
$self->session->log->warn("Contains invalid tokens: $rest");
return;
}
$self->session->log->debug("Processed as: $rest");
return {
target => $target,
expression => $rest,
};
return \%lookup;
}
#-------------------------------------------------------------------
@ -915,11 +874,12 @@ sub nextQuestions {
my $section = $self->nextResponseSection();
my $sectionIndex = $self->nextResponseSectionIndex;
my $questionsPerPage = $self->survey->section( [ $self->nextResponseSectionIndex ] )->{questionsPerPage};
# Get all of the existing question responses (so that we can do Section and Question [[var]] replacements
my $recordedResponses = $self->recordedResponses();
my $responseValuesByVariableName = $self->responseValuesByVariableName();
# Do text replacement
$section->{text} = $self->getTemplatedText($section->{text}, $recordedResponses);
$section->{text} = $self->getTemplatedText($section->{text}, $responseValuesByVariableName);
# Collect all the questions to be shown on the next page..
my @questions;
@ -942,7 +902,7 @@ sub nextQuestions {
my %questionCopy = %{$self->survey->question( $address )};
# Do text replacement
$questionCopy{text} = $self->getTemplatedText($questionCopy{text}, $recordedResponses);
$questionCopy{text} = $self->getTemplatedText($questionCopy{text}, $responseValuesByVariableName);
# Add any extra fields we want..
$questionCopy{id} = $self->questionId($sIndex, $qIndex);
@ -954,7 +914,7 @@ sub nextQuestions {
my %answerCopy = %{ $self->survey->answer( [ $sIndex, $qIndex, $aIndex ] ) };
# Do text replacement
$answerCopy{text} = $self->getTemplatedText($answerCopy{text}, $recordedResponses);
$answerCopy{text} = $self->getTemplatedText($answerCopy{text}, $responseValuesByVariableName);
# Add any extra fields we want..
$answerCopy{id} = $self->answerId($sIndex, $qIndex, $aIndex);
@ -1086,7 +1046,116 @@ sub aIndexes {
#-------------------------------------------------------------------
=head2 returnResponsesForReporting
=head2 showSummary ( [$sectionAddresses] )
showSummary returns the current responses summary for the entire response, if
no address is passed in, or just the sections addressed by $sectionAddresses.
For each section, the total correct, wrong, time taken, and points are returned. And each
question is listed with the text, given score, user response, and if it was correct.
This list is meant for a template and only what is needed should be shown.
A summary of the entire suvey,
=cut
sub showSummary{
my $self = shift;
my $sectionAddies = shift;#array of section addresses
my $all = 0;
$all = 1 if(! $sectionAddies);
my ($summaries);
my $responses = $self->recordedResponses();
my %goodSection;
map{$goodSection{$_} = 1} @$sectionAddies;
return if(! $responses);
my ($sectionIndex, $questionIndex, $answerIndex) = (-1, -1, -1);
my ($currentSection,$currentQuestion) = (-1, -1);
($summaries->{totalCorrect},$summaries->{totalIncorrect}) = (0,0);
for my $response (@$responses){
if(! $all and ! $goodSection{$response->{address}->[0]}){next;}
if($response->{isCorrect}){
$summaries->{totalCorrect}++;
}else{
$summaries->{totalIncorrect}++;
}
$summaries->{totalAnswers}++;
if($currentSection != $response->{address}->[0]){
$summaries->{totalSections}++;
$sectionIndex++;
$questionIndex = -1;
$answerIndex = -1;
$currentQuestion = -1;
$currentSection = $response->{address}->[0];
_loadSectionIntoSummary(\%{$summaries->{sections}->[$sectionIndex]},$response);
}
if($currentQuestion != $response->{address}->[1]){
$summaries->{totalQuestions}++;
$questionIndex++;
$answerIndex = -1;
$currentQuestion = $response->{address}->[1];
_loadQuestionIntoSummary(\%{$summaries->{sections}->[$sectionIndex]->{questions}->[$questionIndex]},$response);
}
$answerIndex++;
_loadAnswerIntoSummary(\%{$summaries->{sections}->[$sectionIndex]->{questions}->[$questionIndex]->{answers}->[$answerIndex]},
$response,
$self->survey->{multipleChoiceTypes});
}
return $summaries;
}
sub _loadAnswerIntoSummary{
my $node = shift;
my $response = shift;
my $types = shift;
$node->{id} = $response->{address}->[2] + 1;
if($response->{isCorrect}){
$node->{iscorrect} = 1;
$node->{score} = $response->{value};
}else{
$node->{iscorrect} = 0;
$node->{score} = 0;
}
$node->{text} = $response->{answerText};
#test if it is a multiple choide type
if($types->{$response->{questionType}}){
$node->{value} = $response->{value};
}else{
$node->{value} = $response->{recordedValue};
}
}
sub _loadQuestionIntoSummary{
my $node = shift;
my $response = shift;
$node->{id} = $response->{address}->[1] + 1;
$node->{text} = $response->{questionText};
}
sub _loadSectionIntoSummary{
my $node = shift;
my $response = shift;
$node->{id} = $response->{address}->[0] + 1;
$node->{inCorrect} = 0 if(!defined $node->{section}->{inCorrect});
$node->{score} = 0 if(!defined $node->{section}->{score});
$node->{correct} = 0 if(!defined $node->{section}->{correct});
if($response->{isCorrect}){
$node->{score} += $response->{value};
$node->{correct}++;
}else{
$node->{inCorrect}++;
}
}
#-------------------------------------------------------------------
=head2 returnResponseForReporting
Used to extract JSON responses for use in reporting results.
@ -1096,7 +1165,7 @@ recorded value, and the id of the answer.
=cut
# TODO: This sub should make use of recordedResponses
# TODO: This sub should make use of responseValuesByVariableName
sub returnResponseForReporting {
my $self = shift;

View file

@ -48,79 +48,18 @@ likely operate on the question indexed by:
use strict;
use JSON;
use Data::Dumper;
use Params::Validate qw(:all);
Params::Validate::validation_options( on_fail => sub { WebGUI::Error::InvalidParam->throw( error => shift ) } );
# N.B. We're currently using Storable::dclone instead of Clone::clone
# because Colin uncovered some Clone bugs in Perl 5.10
#use Clone qw/clone/;
use Storable qw/dclone/;
use Clone qw/clone/;
# The maximum value of questionsPerPage is currently hardcoded here
my $MAX_QUESTIONS_PER_PAGE = 20;
my %MULTI_CHOICE_BUNDLES = (
'Agree/Disagree' => [ 'Strongly disagree', (q{}) x 5, 'Strongly agree' ],
Certainty => [ 'Not at all certain', (q{}) x 9, 'Extremely certain' ],
Concern => [ 'Not at all concerned', (q{}) x 9, 'Extremely concerned' ],
Confidence => [ 'Not at all confident', (q{}) x 9, 'Extremely confident' ],
Education => [
'Elementary or some high school',
'High school/GED',
'Some college/vocational school',
'College graduate',
'Some graduate work',
'Master\'s degree',
'Doctorate (of any type)',
'Other degree (verbatim)',
],
Effectiveness => [ 'Not at all effective', (q{}) x 9, 'Extremely effective' ],
Gender => [qw( Male Female )],
Ideology => [
'Strongly liberal',
'Liberal',
'Somewhat liberal',
'Middle of the road',
'Slightly conservative',
'Conservative',
'Strongly conservative'
],
Importance => [ 'Not at all important', (q{}) x 9, 'Extremely important' ],
Likelihood => [ 'Not at all likely', (q{}) x 9, 'Extremely likely' ],
'Oppose/Support' => [ 'Strongly oppose', (q{}) x 5, 'Strongly support' ],
Party =>
[ 'Democratic party', 'Republican party (or GOP)', 'Independent party', 'Other party (verbatim)' ],
Race =>
[ 'American Indian', 'Asian', 'Black', 'Hispanic', 'White non-Hispanic', 'Something else (verbatim)' ],
Risk => [ 'No risk', (q{}) x 9, 'Extreme risk' ],
Satisfaction => [ 'Not at all satisfied', (q{}) x 9, 'Extremely satisfied' ],
Security => [ 'Not at all secure', (q{}) x 9, 'Extremely secure' ],
Threat => [ 'No threat', (q{}) x 9, 'Extreme threat' ],
'True/False' => [qw( True False )],
'Yes/No' => [qw( Yes No )],
Scale => [q{}],
'Multiple Choice' => [q{}],
);
my @SPECIAL_QUESTION_TYPES = (
'Dual Slider - Range',
'Multi Slider - Allocate',
'Slider',
'Currency',
'Email',
'Phone Number',
'Text',
'Text Date',
'TextArea',
'File Upload',
'Date',
'Date Range',
'Hidden',
);
sub specialQuestionTypes {
return @SPECIAL_QUESTION_TYPES;
}
#sub specialQuestionTypes {
# return @SPECIAL_QUESTION_TYPES;
#}
=head2 new ( $session, json )
@ -153,6 +92,9 @@ sub new {
bless $self, $class;
#Load question types
$self->loadTypes();
# Initialise the survey data structure if empty..
if ( $self->totalSections == 0 ) {
$self->newObject( [] );
@ -160,6 +102,78 @@ sub new {
return $self;
}
=head2 loadTypes
Loads the Multiple Choice and Special Question types
=cut
sub loadTypes {
my $self = shift;
@{$self->{specialQuestionTypes}} = (
'Dual Slider - Range',
'Multi Slider - Allocate',
'Slider',
'Currency',
'Email',
'Phone Number',
'Text',
'Text Date',
'TextArea',
'File Upload',
'Date',
'Date Range',
'Year Month',
'Hidden',
);
my $refs = $self->session->db->buildArrayRefOfHashRefs("SELECT questionType, answers FROM Survey_questionTypes");
map($self->{multipleChoiceTypes}->{$_->{questionType}} = [split/,/,$_->{answers}], @$refs);
}
sub addType {
my $self = shift;
my $name = shift;
my $address = shift;
my $obj = $self->getObject($address);
my @answers;
for my $ans(@{$obj->{answers}}){
push(@answers,$ans->{text});
}
my $ansString = join(',',@answers);
$self->session->db->write("INSERT INTO Survey_questionTypes VALUES(?,?) ON DUPLICATE KEY UPDATE answers = ?",[$name,$ansString,$ansString]);
$self->question($address)->{questionType} = $name;
}
sub removeType {
my $self = shift;
my $address = shift;
my $obj = $self->getObject($address);
$self->session->db->write("DELETE FROM Survey_questionTypes WHERE questionType = ?",[$obj->{questionType}]);
}
=head2 specialQuestionTypes
Returns the arrayref to the special question types
=cut
sub specialQuestionTypes {
my $self = shift;
return $self->{specialQuestionTypes};
}
=head2 multipleChoiceTypes
Returns the hashref to the multiple choice types
=cut
sub multipleChoiceTypes {
my $self = shift;
return $self->{multipleChoiceTypes};
}
=head2 freeze
Serialize this Perl object into a JSON string. The serialized object is made up of the survey and sections
@ -350,13 +364,13 @@ sub getObject {
return if !$count;
if ( $count == 1 ) {
return dclone $self->sections->[ sIndex($address) ];
return clone $self->sections->[ sIndex($address) ];
}
elsif ( $count == 2 ) {
return dclone $self->sections->[ sIndex($address) ]->{questions}->[ qIndex($address) ];
return clone $self->sections->[ sIndex($address) ]->{questions}->[ qIndex($address) ];
}
else {
return dclone $self->sections->[ sIndex($address) ]->{questions}->[ qIndex($address) ]->{answers}
return clone $self->sections->[ sIndex($address) ]->{questions}->[ qIndex($address) ]->{answers}
->[ aIndex($address) ];
}
}
@ -403,12 +417,14 @@ sub getGotoTargets {
# Valid goto targets are all of the section variable names..
my @section_vars = map {$_->{variable}} @{$self->sections};
# ..and all of the question variable names..
my @question_vars = map {$_->{variable}} @{$self->questions};
# ..excluding the ones that are empty
return grep { $_ ne q{} } (@section_vars, @question_vars);
my @grep = grep { $_ ne q{} } (@section_vars, @question_vars);
return \@grep;
#return grep { $_ ne q{} } (@section_vars, @question_vars);
}
=head2 getSectionEditVars ( $address )
@ -512,7 +528,6 @@ sub getQuestionEditVars {
# Change questionType from a single element into an array of hashrefs which list the available
# question types and which one is currently selected for this question..
for my $qType ($self->getValidQuestionTypes) {
push @{ $var{questionType} }, {
text => $qType,
@ -529,7 +544,8 @@ A convenience method. Returns a list of question types.
=cut
sub getValidQuestionTypes {
return sort (@SPECIAL_QUESTION_TYPES, keys %MULTI_CHOICE_BUNDLES);
my $self = shift;
return sort (@{$self->{specialQuestionTypes}}, keys %{$self->{multipleChoiceTypes}});
}
=head2 getAnswerEditVars ( $address )
@ -761,14 +777,14 @@ sub copy {
if ( $count == 1 ) {
# Clone the indexed section onto the end of the list of sections..
push @{ $self->sections }, dclone $self->section($address);
push @{ $self->sections }, clone $self->section($address);
# Update $address with the index of the newly created section
$address->[0] = $self->lastSectionIndex;
}
elsif ( $count == 2 ) {
# Clone the indexed question onto the end of the list of questions..
push @{ $self->questions($address) }, dclone $self->question($address);
push @{ $self->questions($address) }, clone $self->question($address);
# Update $address with the index of the newly created question
$address->[1] = $self->lastQuestionIndex($address);
@ -1002,7 +1018,7 @@ sub getMultiChoiceBundle {
my $self = shift;
my ($type) = validate_pos( @_, { type => SCALAR | UNDEF } );
return $MULTI_CHOICE_BUNDLES{$type};
return $self->{multipleChoiceTypes}->{$type};
}
=head2 addAnswersToQuestion ($address, $answers, $verbatims)
@ -1047,7 +1063,7 @@ sub addAnswersToQuestion {
$self->update(
\@address_copy,
{ text => $answers->[$answer_index],
recordedAnswer => $answer_index + 1,
recordedAnswer => $answer_index + 1, # 1-indexed
verbatim => $verbatims->{$answer_index},
}
);
@ -1176,6 +1192,123 @@ sub totalAnswers {
}
}
=head2 validateSurvey ()
Returns an array of messages to inform a user what is logically wrong with the Survey
=cut
sub validateSurvey{
my $self = shift;
#check all goto's
#bad goto expressions
#check that all survey is able to be seen
my @messages;
#set up valid goto targets
my $gotoTargets = $self->getGotoTargets();
my $goodTargets;
my $duplicateTargets;
for my $g (@{$gotoTargets}) {
$goodTargets->{$g}++;
$duplicateTargets->{$g}++ if $goodTargets->{$g} > 1;
}
#step through each section validating it.
my $sections = $self->sections();
for(my $s = 0; $s <= $#$sections; $s++){
my $sNum = $s + 1;
my $section = $self->section([$s]);
if(! $self->validateGoto($section,$goodTargets)){
push @messages,"Section $sNum has invalid Jump target: \"$section->{goto}\"";
}
if(! $self->validateGotoInfiniteLoop($section)){
push @messages,"Section $sNum jumps to itself.";
}
if(my $error = $self->validateGotoExpression($section,$goodTargets)){
push @messages,"Section $sNum has invalid Jump Expression: \"$section->{gotoExpression}\". Error: $error";
}
if (my $var = $section->{variable}) {
if (my $count = $duplicateTargets->{$var}) {
push @messages, "Section $sNum variable name $var is re-used in $count other place(s).";
}
}
#step through each question validating it.
my $questions = $self->questions([$s]);
for(my $q = 0; $q <= $#$questions; $q++){
my $qNum = $q + 1;
my $question = $self->question([$s,$q]);
if(! $self->validateGoto($question,$goodTargets)){
push @messages,"Section $sNum Question $qNum has invalid Jump target: \"$question->{goto}\"";
}
if(! $self->validateGotoInfiniteLoop($question)){
push @messages,"Section $sNum Question $qNum jumps to itself.";
}
if(my $error = $self->validateGotoExpression($question,$goodTargets)){
push @messages,"Section $sNum Question $qNum has invalid Jump Expression: \"$question->{gotoExpression}\". Error: $error";
}
if($#{$question->{answers}} < 0){
push @messages,"Section $sNum Question $qNum does not have any answers.";
}
if(! $question->{text} =~ /\w/){
push @messages,"Section $sNum Question $qNum does not have any text.";
}
if (my $var = $question->{variable}) {
if (my $count = $duplicateTargets->{$var}) {
push @messages, "Section $sNum Question $qNum variable name $var is re-used in $count other place(s).";
}
}
#step through each answer validating it.
my $answers = $self->answers([$s,$q]);
for(my $a = 0; $a <= $#$answers; $a++){
my $aNum = $a + 1;
my $answer = $self->answer([$s,$q,$a]);
if(! $self->validateGoto($answer,$goodTargets)){
push @messages,"Section $sNum Question $qNum Answer $aNum has invalid Jump target: \"$answer->{goto}\"";
}
if(! $self->validateGotoInfiniteLoop($answer)){
push @messages,"Section $sNum Question $qNum Answer $aNum jumps to itself.";
}
if(my $error = $self->validateGotoExpression($answer,$goodTargets)){
push @messages,"Section $sNum Question $qNum Answer $aNum has invalid Jump Expression: \"$answer->{gotoExpression}\". Error: $error";
}
}
}
}
return \@messages;
}
sub validateGoto{
my $self = shift;
my $object = shift;
my $goodTargets = shift;
return 0 if($object->{goto} =~ /\w/ && ! exists($goodTargets->{$object->{goto}}));
return 1;
}
sub validateGotoInfiniteLoop{
my $self = shift;
my $object = shift;
return 0 if($object->{goto} =~ /\w/ and $object->{goto} eq $object->{variable});
return 1;
}
sub validateGotoExpression{
my $self = shift;
my $object = shift;
my $goodTargets = shift;
return unless $object->{gotoExpression};
use WebGUI::Asset::Wobject::Survey::ExpressionEngine;
my $engine = "WebGUI::Asset::Wobject::Survey::ExpressionEngine";
return $engine->run($self->session, $object->{gotoExpression}, { validate => 1, validTargets => $goodTargets } );
}
=head2 section ($address)
Returns a reference to one section.
@ -1208,9 +1341,9 @@ sub session {
Returns a reference to all the questions from a particular section.
=head3 $address
=head3 $address (optional)
See L<"Address Parameter">.
See L<"Address Parameter">. If not defined, returns all questions.
=cut
@ -1218,7 +1351,13 @@ sub questions {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF, optional => 1});
return $self->sections->[ $address->[0] ]->{questions};
if ($address) {
return $self->sections->[ $address->[0] ]->{questions};
} else {
my $questions;
push @$questions, @{$_->{questions} || []} for @{$self->sections};
return $questions;
}
}
=head2 question ($address)

View file

@ -17,7 +17,8 @@ use WebGUI::Cache;
use WebGUI::Exception;
use WebGUI::HTML;
use WebGUI::International;
use base 'WebGUI::Asset::Wobject';
use Class::C3;
use base qw(WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject);
use WebGUI::Macro;
use XML::FeedPP;
@ -116,7 +117,7 @@ sub definition {
className=>'WebGUI::Asset::Wobject::SyndicatedContent',
properties=>\%properties
});
return $class->SUPER::definition($session, $definition);
return $class->next::method($session, $definition);
}
#-------------------------------------------------------------------
@ -129,6 +130,7 @@ Combines all feeds into a single XML::FeedPP object.
sub generateFeed {
my $self = shift;
my $limit = shift || $self->get('maxHeadlines');
my $feed = XML::FeedPP::Atom->new();
my $log = $self->session->log;
@ -173,8 +175,8 @@ sub generateFeed {
# sort them by date
$feed->sort_item();
# limit the feed to the maxium number of headlines
$feed->limit_item($self->get('maxHeadlines'));
# limit the feed to the maximum number of headlines (or the feed generator limit).
$feed->limit_item($limit);
# mark this asset as updated
$self->update({}) if ($newlyCached);
@ -184,6 +186,52 @@ sub generateFeed {
#-------------------------------------------------------------------
=head2 getFeed ()
Override the one in the parent...
=cut
sub getFeed {
my $self = shift;
my $feed = shift;
foreach my $item ($self->generateFeed( $self->get('itemsPerFeed') )->get_item) {
my $set_permalink_false = 0;
my $new_item = $feed->add_item( $item );
if (!$new_item->guid) {
if ($new_item->link) {
$new_item->guid( $new_item->link );
} else {
$new_item->guid( $self->session->id->generate );
$set_permalink_false = 1;
}
}
$new_item->guid( $new_item->guid, isPermaLink => 0 ) if $set_permalink_false;
}
$feed->title( $self->get('feedTitle') || $self->get('title') );
$feed->description( $self->get('feedDescription') || $self->get('synopsis') );
$feed->pubDate( $self->getContentLastModified );
$feed->copyright( $self->get('feedCopyright') );
$feed->link( $self->getUrl );
# $feed->language( $lang );
if ($self->get('feedImage')) {
my $storage = WebGUI::Storage->get($self->session, $self->get('feedImage'));
my @files = @{ $storage->getFiles };
if (scalar @files) {
$feed->image(
$storage->getUrl( $files[0] ),
$self->get('feedImageDescription') || $self->getTitle,
$self->get('feedImageUrl') || $self->getUrl,
$self->get('feedImageDescription') || $self->getTitle,
( $storage->getSizeInPixels( $files[0] ) ) # expands to width and height
);
}
}
return $feed;
}
#-------------------------------------------------------------------
=head2 getTemplateVariables
Returns a hash reference of template variables.
@ -198,11 +246,11 @@ sub getTemplateVariables {
my ($self, $feed) = @_;
my @items = $feed->get_item;
my %var;
$var{channel_title} = WebGUI::HTML::filter($feed->title, 'javascript');
$var{channel_title} = WebGUI::HTML::filter(scalar $feed->title, 'javascript');
$var{channel_description} = WebGUI::HTML::filter(scalar($feed->description), 'javascript');
$var{channel_date} = WebGUI::HTML::filter(scalar($feed->get_pubDate_epoch), 'javascript');
$var{channel_copyright} = WebGUI::HTML::filter(scalar($feed->copyright), 'javascript');
$var{channel_link} = WebGUI::HTML::filter($feed->link, 'javascript');
$var{channel_link} = WebGUI::HTML::filter(scalar $feed->link, 'javascript');
my @image = $feed->image;
$var{channel_image_url} = WebGUI::HTML::filter($image[0], 'javascript');
$var{channel_image_title} = WebGUI::HTML::filter($image[1], 'javascript');
@ -212,12 +260,12 @@ sub getTemplateVariables {
$var{channel_image_height} = WebGUI::HTML::filter($image[5], 'javascript');
foreach my $object (@items) {
my %item;
$item{title} = WebGUI::HTML::filter($object->title, 'javascript');
$item{date} = WebGUI::HTML::filter($object->get_pubDate_epoch, 'javascript');
$item{category} = WebGUI::HTML::filter($object->category, 'javascript');
$item{author} = WebGUI::HTML::filter($object->author, 'javascript');
$item{guid} = WebGUI::HTML::filter($object->guid, 'javascript');
$item{link} = WebGUI::HTML::filter($object->link, 'javascript');
$item{title} = WebGUI::HTML::filter(scalar $object->title, 'javascript');
$item{date} = WebGUI::HTML::filter(scalar $object->get_pubDate_epoch, 'javascript');
$item{category} = WebGUI::HTML::filter(scalar $object->category, 'javascript');
$item{author} = WebGUI::HTML::filter(scalar $object->author, 'javascript');
$item{guid} = WebGUI::HTML::filter(scalar $object->guid, 'javascript');
$item{link} = WebGUI::HTML::filter(scalar $object->link, 'javascript');
$item{description} = WebGUI::HTML::filter(scalar($object->description), 'javascript');
$item{descriptionFirst100words} = $item{description};
$item{descriptionFirst100words} =~ s/(((\S+)\s+){100}).*/$1/s;
@ -256,15 +304,10 @@ See WebGUI::Asset::prepareView() for details.
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
$self->next::method;
my $template = WebGUI::Asset::Template->new($self->session, $self->get("templateId"));
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
my $title = $self->get("title");
my $style = $self->session->style;
$style->setLink($self->getUrl("func=viewRss"), { rel=>'alternate', type=>'application/rss+xml', title=>$title.' (RSS)' });
$style->setLink($self->getUrl("func=viewRdf"), { rel=>'alternate', type=>'application/rdf+xml', title=>$title.' (RDF)' });
$style->setLink($self->getUrl("func=viewAtom"), { rel=>'alternate', type=>'application/atom+xml', title=>$title.' (Atom)' });
}
@ -279,7 +322,7 @@ See WebGUI::Asset::purgeCache() for details.
sub purgeCache {
my $self = shift;
WebGUI::Cache->new($self->session,"view_".$self->getId)->delete;
$self->SUPER::purgeCache;
$self->next::method;
}
#-------------------------------------------------------------------
@ -318,59 +361,7 @@ See WebGUI::Asset::Wobject::www_view() for details.
sub www_view {
my $self = shift;
$self->session->http->setCacheControl($self->get("cacheTimeout"));
$self->SUPER::www_view(@_);
}
#-------------------------------------------------------------------
=head2 www_viewAtom ( )
Emit an Atom 0.3 feed.
=cut
sub www_viewAtom {
my $self = shift;
my $feed = $self->generateFeed;
my $atom = XML::FeedPP::Atom->new;
$atom->merge($feed);
$self->session->http->setMimeType('application/atom+xml');
return $atom->to_string;
}
#-------------------------------------------------------------------
=head2 www_viewRdf ( )
Emit an RSS 1.0 / RDF feed.
=cut
sub www_viewRdf {
my $self = shift;
my $feed = $self->generateFeed;
my $rdf = XML::FeedPP::RDF->new;
$rdf->merge($feed);
$self->session->http->setMimeType('application/rdf+xml');
return $rdf->to_string;
}
#-------------------------------------------------------------------
=head2 www_viewRss ( )
Emit an RSS 2.0 feed.
=cut
sub www_viewRss {
my $self = shift;
my $feed = $self->generateFeed;
my $rss = XML::FeedPP::RSS->new;
$rss->merge($feed);
$self->session->http->setMimeType('application/rss+xml');
return $rss->to_string;
$self->next::method(@_);
}
#-------------------------------------------------------------------
@ -412,6 +403,20 @@ sub www_viewRSS10 {
return $self->www_viewRdf;
}
#-------------------------------------------------------------------
=head2 www_viewRSS ( )
Deprecated. Use www_viewRss() instead.
=cut
sub www_viewRSS {
my $self = shift;
return $self->www_viewRss;
}
#-------------------------------------------------------------------
=head2 www_viewRSS20 ( )

View file

@ -308,6 +308,42 @@ sub duplicate {
return $newAsset;
}
#-------------------------------------------------------------------
=head2 duplicateThing ( thingId )
Duplicates a thing.
=head3 thingId
The id of the Thing that will be duplicated.
=cut
sub duplicateThing {
my $self = shift;
my $oldThingId = shift;
my $db = $self->session->db;
my $thingProperties = $self->getThing($oldThingId);
$thingProperties->{thingId} = 'new';
$thingProperties->{label} = $thingProperties->{label}.' (copy)';
my $newThingId = $self->addThing($thingProperties);
my $fields = $db->buildArrayRefOfHashRefs('select * from Thingy_fields where assetId=? and thingId=?'
,[$self->getId,$oldThingId]);
foreach my $field (@$fields) {
# set thingId to newly created thing's id.
$field->{thingId} = $newThingId;
$self->addField($field,0);
}
return $newThingId;
}
#-------------------------------------------------------------------
=head2 deleteField ( fieldId , thingId )
@ -641,7 +677,10 @@ sub getEditFieldForm {
}
my $dialogPrefix;
if ($fieldId eq "new"){
if ($field->{oldFieldId}){
$dialogPrefix = "edit_".$field->{oldFieldId}."_Dialog_copy";
}
elsif($fieldId eq "new"){
$dialogPrefix = "addDialog";
}
else{
@ -1436,6 +1475,26 @@ sub www_deleteFieldConfirm {
return 1;
}
#-------------------------------------------------------------------
=head2 www_duplicateThing ( )
Duplicates a Thing.
=cut
sub www_duplicateThing {
my $self = shift;
my $session = $self->session;
my $thingId = $session->form->process("thingId");
return $session->privilege->insufficient() unless $self->canEdit;
$self->duplicateThing($thingId);
return $self->www_manage;
}
#-------------------------------------------------------------------
=head2 www_copyThingData( )
@ -1714,7 +1773,10 @@ sub www_editThing {
." <td style='width:100px;' valign='top' class='formDescription'>".$field->{label}."</td>\n"
." <td style='width:370px;'>".$formElement."</td>\n"
." <td style='width:120px;' valign='top'> <input onClick=\"editListItem('".$self->session->url->page()
."?func=editField;fieldId=".$field->{fieldId}.";thingId=".$thingId."','".$field->{fieldId}."')\" value='".$i18n->get('Edit','Icon')."' type='button'>"
."?func=editField;fieldId=".$field->{fieldId}.";thingId=".$thingId."','".$field->{fieldId}."')\" value='Edit' type='button'>"
." <input onClick=\"editListItem('".$self->session->url->page()
."?func=editField;copy=1;fieldId=".$field->{fieldId}.";thingId=".$thingId."','".$field->{fieldId}
."','copy')\" value='Copy' type='button'>"
."<input onClick=\"deleteListItem('".$self->session->url->page()."','".$field->{fieldId}."','".$thingId."')\" "
."value='".$i18n->get('Delete','Icon')."' type='button'></td>\n</tr>\n</table>\n</li>\n";
@ -2055,12 +2117,19 @@ Returns the html for a pop-up dialog to add or edit a field.
sub www_editField {
my $self = shift;
my $self = shift;
my $session = $self->session;
my (%properties,$thingId,$fieldId,$dialogBody);
return $self->session->privilege->insufficient() unless $self->canEdit;
$fieldId = $self->session->form->process("fieldId");
$thingId = $self->session->form->process("thingId");
%properties = $self->session->db->quickHash("select * from Thingy_fields where thingId=".$self->session->db->quote($thingId)." and fieldId = ".$self->session->db->quote($fieldId)." and assetId = ".$self->session->db->quote($self->get("assetId")));
return $session->privilege->insufficient() unless $self->canEdit;
$fieldId = $session->form->process("fieldId");
$thingId = $session->form->process("thingId");
%properties = $session->db->quickHash("select * from Thingy_fields where thingId=? and fieldId=? and assetId=?",
[$thingId,$fieldId,$self->get("assetId")]);
if($session->form->process("copy")){
$properties{oldFieldId} = $properties{fieldId};
$properties{fieldId} = 'new';
$properties{label} = $properties{label}.' (copy)';
}
$dialogBody = $self->getEditFieldForm(\%properties);
$self->session->output->print($dialogBody->print);
return "chunked";
@ -2868,6 +2937,8 @@ sub www_manage {
"",$i18n->get('delete thing warning')),
'thing_editUrl' => $session->url->append($url, 'func=editThing;thingId='.$thing->{thingId}),
'thing_editIcon' => $session->icon->edit('func=editThing;thingId='.$thing->{thingId}),
'thing_copyUrl' => $session->url->append($url, 'func=duplicateThing;thingId='.$thing->{thingId}),
'thing_copyIcon' => $session->icon->copy('func=duplicateThing;thingId='.$thing->{thingId}),
'thing_addUrl' => $session->url->append($url,
'func=editThingData;thingId='.$thing->{thingId}.';thingDataId=new'),
'thing_searchUrl' => $session->url->append($url, 'func=search;thingId='.$thing->{thingId}),

View file

@ -10,7 +10,8 @@ package WebGUI::Asset::Wobject::WikiMaster;
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use base 'WebGUI::Asset::Wobject';
use Class::C3;
use base qw(WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject);
use strict;
use Tie::IxHash;
use WebGUI::International;
@ -70,6 +71,7 @@ sub appendRecentChanges {
username=>$user->username,
date=>$self->session->datetime->epochToHuman($asset->get("revisionDate")),
isAvailable=>$isAvailable,
assetId=>$id,
});
}
}
@ -139,7 +141,7 @@ sub autolinkHtml {
#-------------------------------------------------------------------
sub canAdminister {
my $self = shift;
return $self->session->user->isInGroup($self->get('groupToAdminister')) || $self->SUPER::canEdit;
return $self->session->user->isInGroup($self->get('groupToAdminister')) || $self->WebGUI::Asset::Wobject::canEdit;
}
#-------------------------------------------------------------------
@ -164,7 +166,7 @@ sub canEdit {
) &&
$self->canEditPages
) || # account for new posts
$self->SUPER::canEdit()
$self->next::method()
);
}
@ -337,13 +339,44 @@ sub definition {
properties => \%properties,
};
return $class->SUPER::definition($session, $definition);
return $class->next::method($session, $definition);
}
#-------------------------------------------------------------------
=head2 getRssFeedItems ()
Returns an array reference of hash references. Each hash reference has a title,
description, link, and date field. The date field can be either an epoch date, an RFC 1123
date, or a ISO date in the format of YYYY-MM-DD HH:MM::SS. Optionally specify an
author, and a guid field.
=cut
sub getRssFeedItems {
my $self = shift;
my $vars = {};
$self->appendRecentChanges( $vars, $self->get('itemsPerFeed') );
my $var = [];
foreach my $item ( @{ $vars->{recentChanges} } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $self->session, $item->{assetId} );
push @{ $var }, {
'link' => $asset->getUrl,
'guid' => $item->{ 'assetId' } . $asset->get( 'revisionDate' ),
'title' => $asset->getTitle,
'description' => $item->{ 'actionTaken' },
'date' => $item->{ 'date' },
'author' => $item->{ 'username' },
};
}
return $var;
}
#-------------------------------------------------------------------
sub prepareView {
my $self = shift;
$self->SUPER::prepareView;
$self->next::method;
$self->{_frontPageTemplate} =
WebGUI::Asset::Template->new($self->session, $self->get('frontPageTemplateId'));
$self->{_frontPageTemplate}->prepare;
@ -355,7 +388,7 @@ sub processPropertiesFromFormPost {
my $groupsChanged =
(($self->session->form->process('groupIdView') ne $self->get('groupIdView'))
or ($self->session->form->process('groupIdEdit') ne $self->get('groupIdEdit')));
my $ret = $self->SUPER::processPropertiesFromFormPost(@_);
my $ret = $self->next::method(@_);
if ($groupsChanged) {
foreach my $child (@{$self->getLineage(['children'], {returnObjects => 1})}) {
$child->update({ groupIdView => $self->get('groupIdView'),

View file

@ -18,6 +18,10 @@ use WebGUI::International;
use WebGUI::Utility;
use base 'WebGUI::Asset::Wobject';
# To get an installer for your wobject, add the Installable AssetAspect
# See WebGUI::AssetAspect::Installable and sbin/installClass.pl for more
# details
#-------------------------------------------------------------------
=head2 definition ( )
@ -29,46 +33,43 @@ getEditForm method is unnecessary/redundant/useless.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, 'Asset_NewWobject');
my %properties;
tie %properties, 'Tie::IxHash';
%properties = (
templateId =>{
#See the list of field/control types in /lib/WebGUI/Form/
fieldType=>"template",
defaultValue=>'NewWobjectTmpl00000001',
tab=>"display",
#www_editSave will ignore anyone's attempts to update this field if this is set to 1
noFormPost=>0,
#This is an option specific to the template fieldType.
namespace=>"NewWobject",
#This is what will appear when the user hovers the mouse over the label
# of your form field.
hoverHelp=>$i18n->get('templateId label description'),
# This is the text that will appear to the left of your form field.
label=>$i18n->get('templateId label'),
}
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
icon=>'newWobject.gif',
autoGenerateForms=>1,
tableName=>'NewWobject',
className=>'WebGUI::Asset::Wobject::NewWobject',
properties=>\%properties
});
return $class->SUPER::definition($session, $definition);
}
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new( $session, 'Asset_NewWobject' );
tie my %properties, 'Tie::IxHash', (
templateId => {
#See the list of field/control types in /lib/WebGUI/Form/
fieldType => "template",
defaultValue => 'NewWobjectTmpl00000001',
tab => "display",
#www_editSave will ignore anyone's attempts to update this field if this is set to 1
noFormPost => 0,
#This is an option specific to the template fieldType.
namespace => "NewWobject",
#This is what will appear when the user hovers the mouse over the label
# of your form field.
hoverHelp => $i18n->get('templateId label description'),
# This is the text that will appear to the left of your form field.
label => $i18n->get('templateId label'),
}
);
push @{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'newWobject.gif',
autoGenerateForms => 1,
tableName => 'NewWobject',
className => 'WebGUI::Asset::Wobject::NewWobject',
properties => \%properties
};
return $class->SUPER::definition( $session, $definition );
} ## end sub definition
#-------------------------------------------------------------------
@ -81,9 +82,9 @@ wobject instances, you will need to duplicate them here.
=cut
sub duplicate {
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
return $newAsset;
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
return $newAsset;
}
#-------------------------------------------------------------------
@ -96,16 +97,16 @@ This method is optional if you set autoGenerateForms=1 in the definition.
=cut
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
$tabform->getTab("display")->template(
-value=>$self->getValue("templateId"),
-label=>WebGUI::International::get("template_label","Asset_NewWobject"),
-namespace=>"NewWobject"
);
return $tabform;
$tabform->getTab("display")->template(
value => $self->getValue("templateId"),
label => WebGUI::International::get( "template_label", "Asset_NewWobject" ),
namespace => "NewWobject"
);
return $tabform;
}
#-------------------------------------------------------------------
@ -117,14 +118,13 @@ See WebGUI::Asset::prepareView() for details.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new($self->session, $self->get("templateId"));
$template->prepare;
$self->{_viewTemplate} = $template;
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new( $self->session, $self->get("templateId") );
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 purge ( )
@ -137,10 +137,11 @@ wobject instances, you will need to purge them here.
=cut
sub purge {
my $self = shift;
#purge your wobject-specific data here. This does not include fields
# you create for your NewWobject asset/wobject table.
return $self->SUPER::purge;
my $self = shift;
#purge your wobject-specific data here. This does not include fields
# you create for your NewWobject asset/wobject table.
return $self->SUPER::purge;
}
#-------------------------------------------------------------------
@ -153,16 +154,16 @@ to be displayed within the page style.
=cut
sub view {
my $self = shift;
my $session = $self->session;
my $self = shift;
my $session = $self->session;
#This automatically creates template variables for all of your wobject's properties.
my $var = $self->get;
#This is an example of debugging code to help you diagnose problems.
#WebGUI::ErrorHandler::warn($self->get("templateId"));
return $self->processTemplate($var, undef, $self->{_viewTemplate});
#This automatically creates template variables for all of your wobject's properties.
my $var = $self->get;
#This is an example of debugging code to help you diagnose problems.
#$session->log->warn($self->get("templateId"));
return $self->processTemplate( $var, undef, $self->{_viewTemplate} );
}
#-------------------------------------------------------------------
@ -183,60 +184,6 @@ adminConsole views.
# return $self->getAdminConsole->render($self->getEditForm->print, $i18n->get("edit title"));
#}
#-------------------------------------------------------------------
# Everything below here is to make it easier to install your custom
# wobject, but has nothing to do with wobjects in general
#-------------------------------------------------------------------
# cd /data/WebGUI/lib
# perl -MWebGUI::Asset::Wobject::NewWobject -e install www.example.com.conf [ /path/to/WebGUI ]
# - or -
# perl -MWebGUI::Asset::Wobject::NewWobject -e uninstall www.example.com.conf [ /path/to/WebGUI ]
#-------------------------------------------------------------------
use base 'Exporter';
our @EXPORT = qw(install uninstall);
use WebGUI::Session;
#-------------------------------------------------------------------
sub install {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::Wobject::NewWobject -e install www.example.com.conf\n" unless ($home && $config);
print "Installing asset.\n";
my $session = WebGUI::Session->open($home, $config);
$session->config->addToArray("assets","WebGUI::Asset::Wobject::NewWobject");
$session->db->write("create table NewWobject (
assetId varchar(22) binary not null,
revisionDate bigint not null,
primary key (assetId, revisionDate)
)");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
#-------------------------------------------------------------------
sub uninstall {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::Wobject::NewWobject -e uninstall www.example.com.conf\n" unless ($home && $config);
print "Uninstalling asset.\n";
my $session = WebGUI::Session->open($home, $config);
$session->config->deleteFromArray("assets","WebGUI::Asset::Wobject::NewWobject");
my $rs = $session->db->read("select assetId from asset where className='WebGUI::Asset::Wobject::NewWobject'");
while (my ($id) = $rs->array) {
my $asset = WebGUI::Asset->new($session, $id, "WebGUI::Asset::Wobject::NewWobject");
$asset->purge if defined $asset;
}
$session->db->write("drop table NewWobject");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
1;
#vim:ft=perl

View file

@ -19,7 +19,9 @@ use Tie::IxHash;
use base 'WebGUI::Asset';
use WebGUI::Utility;
# To get an installer for your wobject, add the Installable AssetAspect
# See WebGUI::AssetAspect::Installable and sbin/installClass.pl for more
# details
=head1 NAME
@ -40,21 +42,19 @@ These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 addRevision
This method exists for demonstration purposes only. The superclass
handles revisions to NewAsset Assets.
This method exists for demonstration purposes only. The superclass
handles revisions to NewAsset Assets.
=cut
sub addRevision {
my $self = shift;
my $newSelf = $self->SUPER::addRevision(@_);
return $newSelf;
my $self = shift;
my $newSelf = $self->SUPER::addRevision(@_);
return $newSelf;
}
#-------------------------------------------------------------------
@ -73,66 +73,68 @@ A hash reference passed in from a subclass definition.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my %properties;
tie %properties, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session, "Asset_NewAsset");
%properties = (
templateId => {
# Determines which tab this property appears in
tab=>"display",
#See the list of field/control types in /lib/WebGUI/Form/
fieldType=>"template",
defaultValue=>'NewAssetTmpl0000000001',
#www_editSave will ignore anyone's attempts to update this field if this is set to 1
noFormPost=>0,
#This is an option specific to the template fieldType.
namespace=>"NewAsset",
#This is what will appear when the user hovers the mouse over the label
# of your form field.
hoverHelp=>$i18n->get('templateId label description'),
# This is the text that will appear to the left of your form field.
label=>$i18n->get('templateId label')
},
foo => {
tab=>"properties",
fieldType=>"text",
defaultValue=>undef,
label=>$i18n->get("foo label"),
hoverHelp=>$i18n->get("foo label help")
}
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
icon=>'NewAsset.gif',
autoGenerateForms=>1,
tableName=>'NewAsset',
className=>'WebGUI::Asset::NewAsset',
properties=>\%properties
});
return $class->SUPER::definition($session, $definition);
}
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new( $session, "Asset_NewAsset" );
tie my %properties, 'Tie::IxHash', (
templateId => {
# Determines which tab this property appears in
tab => "display",
#See the list of field/control types in /lib/WebGUI/Form/
fieldType => "template",
defaultValue => 'NewAssetTmpl0000000001',
#www_editSave will ignore anyone's attempts to update this field if this is set to 1
noFormPost => 0,
#This is an option specific to the template fieldType.
namespace => "NewAsset",
#This is what will appear when the user hovers the mouse over the label
# of your form field.
hoverHelp => $i18n->get('templateId label description'),
# This is the text that will appear to the left of your form field.
label => $i18n->get('templateId label')
},
foo => {
tab => "properties",
fieldType => "text",
defaultValue => undef,
label => $i18n->get("foo label"),
hoverHelp => $i18n->get("foo label help")
},
);
push @{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'NewAsset.gif',
autoGenerateForms => 1,
tableName => 'NewAsset',
className => 'WebGUI::Asset::NewAsset',
properties => \%properties,
};
return $class->SUPER::definition( $session, $definition );
} ## end sub definition
#-------------------------------------------------------------------
=head2 duplicate
This method exists for demonstration purposes only. The superclass
handles duplicating NewAsset Assets. This method will be called
whenever a copy action is executed
This method exists for demonstration purposes only. The superclass
handles duplicating NewAsset Assets. This method will be called
whenever a copy action is executed
=cut
sub duplicate {
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
return $newAsset;
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
return $newAsset;
}
#-------------------------------------------------------------------
=head2 indexContent ( )
@ -142,12 +144,11 @@ Making private. See WebGUI::Asset::indexContent() for additonal details.
=cut
sub indexContent {
my $self = shift;
my $indexer = $self->SUPER::indexContent;
$indexer->setIsPublic(0);
my $self = shift;
my $indexer = $self->SUPER::indexContent;
$indexer->setIsPublic(0);
}
#-------------------------------------------------------------------
=head2 prepareView ( )
@ -157,14 +158,13 @@ See WebGUI::Asset::prepareView() for details.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new($self->session, $self->get("templateId"));
$template->prepare;
$self->{_viewTemplate} = $template;
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new( $self->session, $self->get("templateId") );
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 processPropertiesFromFormPost ( )
@ -176,11 +176,10 @@ when /yourAssetUrl?func=editSave is requested/posted.
=cut
sub processPropertiesFromFormPost {
my $self = shift;
$self->SUPER::processPropertiesFromFormPost;
my $self = shift;
$self->SUPER::processPropertiesFromFormPost;
}
#-------------------------------------------------------------------
=head2 purge ( )
@ -194,8 +193,8 @@ asset instances, you will need to purge them here.
=cut
sub purge {
my $self = shift;
return $self->SUPER::purge;
my $self = shift;
return $self->SUPER::purge;
}
#-------------------------------------------------------------------
@ -207,8 +206,8 @@ This method is called when data is purged by the system.
=cut
sub purgeRevision {
my $self = shift;
return $self->SUPER::purgeRevision;
my $self = shift;
return $self->SUPER::purgeRevision;
}
#-------------------------------------------------------------------
@ -220,13 +219,12 @@ method called by the container www_view method.
=cut
sub view {
my $self = shift;
my $var = $self->get; # $var is a hash reference.
$var->{controls} = $self->getToolbar;
return $self->processTemplate($var,undef, $self->{_viewTemplate});
my $self = shift;
my $var = $self->get; # $var is a hash reference.
$var->{controls} = $self->getToolbar;
return $self->processTemplate( $var, undef, $self->{_viewTemplate} );
}
#-------------------------------------------------------------------
=head2 www_edit ( )
@ -238,69 +236,14 @@ the module.
=cut
sub www_edit {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless $self->canEdit;
return $session->privilege->locked() unless $self->canEditIfLocked;
my $i18n = WebGUI::International->new($session, 'Asset_NewAsset');
return $self->getAdminConsole->render($self->getEditForm->print, $i18n->get('edit asset'));
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless $self->canEdit;
return $session->privilege->locked() unless $self->canEditIfLocked;
my $i18n = WebGUI::International->new( $session, 'Asset_NewAsset' );
return $self->getAdminConsole->render( $self->getEditForm->print, $i18n->get('edit asset') );
}
#-------------------------------------------------------------------
# Everything below here is to make it easier to install your custom
# asset, but has nothing to do with assets in general
#-------------------------------------------------------------------
# cd /data/WebGUI/lib
# perl -MWebGUI::Asset::NewAsset -e install www.example.com.conf [ /path/to/WebGUI ]
# - or -
# perl -MWebGUI::Asset::NewAsset -e uninstall www.example.com.conf [ /path/to/WebGUI ]
#-------------------------------------------------------------------
use base 'Exporter';
our @EXPORT = qw(install uninstall);
use WebGUI::Session;
#-------------------------------------------------------------------
sub install {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::NewAsset -e install www.example.com.conf\n" unless ($home && $config);
print "Installing asset.\n";
my $session = WebGUI::Session->open($home, $config);
$session->config->addToArray("assets","WebGUI::Asset::NewAsset");
$session->db->write("create table NewAsset (
assetId varchar(22) binary not null,
revisionDate bigint not null,
primary key (assetId, revisionDate)
)");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
#-------------------------------------------------------------------
sub uninstall {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::NewAsset -e uninstall www.example.com.conf\n" unless ($home && $config);
print "Uninstalling asset.\n";
my $session = WebGUI::Session->open($home, $config);
$session->config->deleteFromArray("assets","WebGUI::Asset::NewAsset");
my $rs = $session->db->read("select assetId from asset where className='WebGUI::Asset::NewAsset'");
while (my ($id) = $rs->array) {
my $asset = WebGUI::Asset->new($session, $id, "WebGUI::Asset::NewAsset");
$asset->purge if defined $asset;
}
$session->db->write("drop table NewAsset");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
1;
#vim:ft=perl

View file

@ -0,0 +1,500 @@
package WebGUI::AssetAspect::RssFeed;
=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 Class::C3;
use WebGUI::Exception;
use WebGUI::Storage;
use XML::FeedPP;
use Path::Class::File;
=head1 NAME
Package WebGUI::AssetAspect::RssFeed
=head1 DESCRIPTION
This is an aspect which exposes an asset's items as an RSS or Atom feed.
=head1 SYNOPSIS
use Class::C3;
use base qw(WebGUI::AssetAspect::RssFeed WebGUI::Asset);
And then wherever you would call $self->SUPER::someMethodName call $self->next::method instead.
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition
Extends the definition to add the RSS fields.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session,'AssetAspect_RssFeed');
my %properties;
tie %properties, 'Tie::IxHash';
%properties = (
itemsPerFeed => {
noFormPost => 0,
fieldType => "integer",
defaultValue => 25,
tab => "rss",
label => $i18n->get('itemsPerFeed'),
hoverHelp => $i18n->get('itemsPerFeed hoverHelp')
},
feedCopyright => {
noFormPost => 0,
fieldType => "text",
defaultValue => "",
tab => "rss",
label => $i18n->get('feedCopyright'),
hoverHelp => $i18n->get('feedCopyright hoverHelp')
},
feedTitle => {
noFormPost => 0,
fieldType => "text",
defaultValue => "",
tab => "rss",
label => $i18n->get('feedTitle'),
hoverHelp => $i18n->get('feedTitle hoverHelp')
},
feedDescription => {
noFormPost => 0,
fieldType => "textarea",
defaultValue => "",
tab => "rss",
label => $i18n->get('feedDescription'),
hoverHelp => $i18n->get('feedDescription hoverHelp')
},
feedImage => {
noFormPost => 0,
fieldType => "image",
tab => "rss",
label => $i18n->get('feedImage'),
hoverHelp => $i18n->get('feedImage hoverHelp')
},
feedImageLink => {
noFormPost => 0,
fieldType => "url",
defaultValue => "",
tab => "rss",
label => $i18n->get('feedImageLink'),
hoverHelp => $i18n->get('feedImageLink hoverHelp')
},
feedImageDescription => {
noFormPost => 0,
fieldType => "text",
defaultValue => "",
tab => "rss",
label => $i18n->get('feedImageDescription'),
hoverHelp => $i18n->get('feedImageDescription hoverHelp')
},
feedHeaderLinks => {
fieldType => "checkList",
allowEmpty => 1,
defaultValue => "rss\natom",
tab => "rss",
options => do {
my %headerLinksOptions;
tie %headerLinksOptions, 'Tie::IxHash';
%headerLinksOptions = (
rss => $i18n->get('rssLinkOption'),
atom => $i18n->get('atomLinkOption'),
rdf => $i18n->get('rdfLinkOption'),
);
\%headerLinksOptions;
},
label => $i18n->get('feedHeaderLinks'),
hoverHelp => $i18n->get('feedHeaderLinks hoverHelp')
},
);
push(@{$definition}, {
autoGenerateForms => 1,
tableName => 'assetAspectRssFeed',
className => 'WebGUI::AssetAspect::RssFeed',
properties => \%properties
});
return $class->next::method($session, $definition);
}
#-------------------------------------------------------------------
=head2 exportAssetCollateral ()
Extended from WebGUI::Asset and exports the www_viewRss() and
www_viewAtom() methods with filenames generated by
getStaticAtomFeedUrl() and getStaticRssFeedUrl().
This method will be called with the following parameters:
=head3 basePath
A L<Path::Class> object representing the base filesystem path for this
particular asset.
=head3 params
A hashref with the quiet, userId, depth, and indexFileName parameters from
L<WebGUI::Asset/exportAsHtml>.
=cut
sub exportAssetCollateral {
# Lots of copy/paste here from AssetExportHtml.pm, since none of the methods there were
# directly useful without ginormous refactoring.
my $self = shift;
my $basepath = shift;
my $args = shift;
my $reportSession = shift;
my $reporti18n = WebGUI::International->new($self->session, 'Asset');
my $basename = $basepath->basename;
my $filedir;
my $filenameBase;
# We want our .rss and .atom files to "appear" at the same level as the asset.
if ($basename eq 'index.html') {
# Get the 2nd ancestor, since the asset url had no dot in it (and it therefore
# had its own directory created for it).
$filedir = $basepath->parent->parent->absolute->stringify;
# Get the parent dir's *path* (essentially the name of the dir) relative to
# its own parent dir.
$filenameBase = $basepath->parent->relative( $basepath->parent->parent )->stringify;
} else {
# Get the 1st ancestor, since the asset is a file recognized by apache, so
# we want our files in the same dir.
$filedir = $basepath->parent->absolute->stringify;
# just use the basename.
$filenameBase = $basename;
}
if ( $reportSession && !$args->{quiet} ) {
$reportSession->output->print('<br />');
}
foreach my $ext (qw( rss atom )) {
my $dest = Path::Class::File->new($filedir, $filenameBase . '.' . $ext);
# tell the user which asset we're exporting.
if ( $reportSession && !$args->{quiet} ) {
my $message = sprintf $reporti18n->get('exporting page'), $dest->absolute->stringify;
$reportSession->output->print(
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $message . '<br />');
}
my $exportSession = WebGUI::Session->open(
$self->session->config->getWebguiRoot,
$self->session->config->getFilename,
undef,
undef,
$self->session->getId,
);
# open another session as the user doing the exporting...
my $selfdupe = WebGUI::Asset->newByDynamicClass( $exportSession, $self->getId );
# next, get the contents, open the file, and write the contents to the file.
my $fh = eval { $dest->open('>:utf8') };
if($@) {
WebGUI::Error->throw(error => "can't open " . $dest->absolute->stringify . " for writing: $!");
$exportSession->close;
}
$exportSession->asset($selfdupe);
$exportSession->output->setHandle($fh);
my $contents;
if ($ext eq 'rss') {
$contents = $selfdupe->www_viewRss;
}
else {
$contents = $selfdupe->www_viewAtom;
} # add more for more extensions.
# chunked content is already printed, no need to print it again
unless($contents eq 'chunked') {
$exportSession->output->print($contents);
}
$exportSession->close;
# tell the user we did this asset collateral correctly
if ( $reportSession && !$args->{quiet} ) {
$reportSession->output->print($reporti18n->get('done'));
}
}
return $self->next::method($basepath, $args, $reportSession);
}
#-------------------------------------------------------------------
=head2 getRssFeedItems ()
This method should throw an exception if it's not overridden. Its intention is
to be overridden by whatever class is using it and should return an array
reference of hash references. Each hash reference should contain at minimum a title,
description, link, and date field. The date field can be either an epoch date, an RFC 1123
date, or a ISO date in the format of YYYY-MM-DD HH:MM::SS. Optionally specify an
author, and a guid field.
=cut
sub getRssFeedItems {
WebGUI::Error::OverrideMe->throw();
}
#-------------------------------------------------------------------
=head2 getAtomFeedUrl ()
Returns $self->getUrl('func=viewAtom').
=cut
sub getAtomFeedUrl {
shift->getUrl("func=viewAtom");
}
#-------------------------------------------------------------------
=head2 getRdfFeedUrl ()
Returns $self->getUrl('func=viewRdf').
=cut
sub getRdfFeedUrl {
shift->getUrl("func=viewRdf");
}
#-------------------------------------------------------------------
=head2 getRssFeedUrl ()
Returns $self->getUrl('func=viewRss').
=cut
sub getRssFeedUrl {
shift->getUrl("func=viewRss");
}
#-------------------------------------------------------------------
=head2 getStaticAtomFeedUrl ()
Returns the current asset's URL with .atom concatenated onto it.
=cut
sub getStaticAtomFeedUrl {
my $self = shift;
my $url = $self->get("url") . '.atom';
$url = $self->session->url->gateway($url);
if ($self->get("encryptPage")) {
$url = $self->session->url->getSiteURL . $url;
$url =~ s/^http:/https:/;
}
return $url;
}
#-------------------------------------------------------------------
=head2 getStaticRdfFeedUrl ()
Returns the current asset's URL with .rdf concatenated onto it.
=cut
sub getStaticRdfFeedUrl {
my $self = shift;
my $url = $self->get("url") . '.rdf';
$url = $self->session->url->gateway($url);
if ($self->get("encryptPage")) {
$url = $self->session->url->getSiteURL . $url;
$url =~ s/^http:/https:/;
}
return $url;
}
#-------------------------------------------------------------------
=head2 getStaticRssFeedUrl ()
Returns the current asset's URL with .rss concatenated onto it.
=cut
sub getStaticRssFeedUrl {
my $self = shift;
my $url = $self->get("url") . '.rss';
$url = $self->session->url->gateway($url);
if ($self->get("encryptPage")) {
$url = $self->session->url->getSiteURL . $url;
$url =~ s/^http:/https:/;
}
return $url;
}
#-------------------------------------------------------------------
=head2 getFeed ()
Adds the syndicated items to the feed; returns the stringified edition.
TODO: convert dates?
=cut
sub getFeed {
my $self = shift;
my $feed = shift;
foreach my $item ( @{ $self->getRssFeedItems } ) {
my $set_permalink_false = 0;
my $new_item = $feed->add_item( %{ $item } );
if (!$new_item->guid) {
if ($new_item->link) {
$new_item->guid( $new_item->link );
} else {
$new_item->guid( $self->session->id->generate );
$set_permalink_false = 1;
}
}
$new_item->guid( $new_item->guid, isPermaLink => 0 ) if $set_permalink_false;
}
$feed->title( $self->get('feedTitle') || $self->get('title') );
$feed->description( $self->get('feedDescription') || $self->get('synopsis') );
$feed->pubDate( $self->getContentLastModified );
$feed->copyright( $self->get('feedCopyright') );
$feed->link( $self->getUrl );
# $feed->language( $lang );
if ($self->get('feedImage')) {
my $storage = WebGUI::Storage->get($self->session, $self->get('feedImage'));
my @files = @{ $storage->getFiles };
if (scalar @files) {
$feed->image(
$storage->getUrl( $files[0] ),
$self->get('feedImageDescription') || $self->getTitle,
$self->get('feedImageUrl') || $self->getUrl,
$self->get('feedImageDescription') || $self->getTitle,
( $storage->getSizeInPixels( $files[0] ) ) # expands to width and height
);
}
}
return $feed;
}
sub prepareView {
my $self = shift;
$self->addHeaderLinks;
return $self->next::method(@_);
}
sub addHeaderLinks {
my $self = shift;
my $style = $self->session->style;
my $title = $self->get('feedTitle') || $self->get("title");
my %feeds = map { $_ => 1 } split /\n/, $self->get('feedHeaderLinks');
my $addType = keys %feeds > 1;
if ($feeds{rss}) {
$style->setLink($self->getRssFeedUrl, {
rel => 'alternate',
type => 'application/rss+xml',
title => $title . ( $addType ? ' (RSS)' : ''),
});
}
if ($feeds{atom}) {
$style->setLink($self->getAtomFeedUrl, {
rel => 'alternate',
type => 'application/atom+xml',
title => $title . ( $addType ? ' (Atom)' : ''),
});
}
if ($feeds{rdf}) {
$style->setLink($self->getRdfFeedUrl, {
rel => 'alternate',
type => 'application/rdf+xml',
title => $title . ( $addType ? ' (RDF)' : ''),
});
}
}
#-------------------------------------------------------------------
=head2 www_viewAtom ()
Return Atom view of the syndicated items.
=cut
sub www_viewAtom {
my $self = shift;
$self->session->http->setMimeType('application/atom+xml');
return $self->getFeed( XML::FeedPP::Atom->new )->to_string;
}
#-------------------------------------------------------------------
=head2 www_viewRdf ()
Return Rdf view of the syndicated items.
=cut
sub www_viewRdf {
my $self = shift;
$self->session->http->setMimeType('application/rdf+xml');
return $self->getFeed( XML::FeedPP::RDF->new )->to_string;
}
#-------------------------------------------------------------------
=head2 www_viewRss ()
Return RSS view of the syndicated items.
=cut
sub www_viewRss {
my $self = shift;
$self->session->http->setMimeType('application/rss+xml');
return $self->getFeed( XML::FeedPP::RSS->new )->to_string;
}
#-------------------------------------------------------------------
=head2 getEditTabs ()
Adds an RSS tab to the Edit Tabs.
=cut
sub getEditTabs {
my $self = shift;
my $i18n = WebGUI::International->new($self->session,'AssetAspect_RssFeed');
return ($self->next::method, ['rss', $i18n->get('RSS tab'), 1]);
}
1;

View file

@ -0,0 +1,81 @@
package WebGUI::AssetCollateral::Sku::Ad::Ad;
=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 base 'WebGUI::Crud';
#------------------------------------------------
=head1 crud_definition
defines the field this crud will contain
userID = the id of the user that purchased the ad
transactionItemid = the id if the transaction item that completes this purchase
adId = th id if the ad purchased
clicksPurchased = the number of clicks the user purchased
impressionsPurchased = the number of impressions the user purchased
dateOfPurchase = the date of purchase
storedImage = storage for the image
isDeleted = boolean that indicates whether the ad has been deleted from the system
=cut
sub crud_definition {
my ($class, $session) = @_;
my $definition = $class->SUPER::crud_definition($session);
$definition->{tableName} = 'adSkuPurchase';
$definition->{tableKey} = 'adSkuPurchaseId';
$definition->{properties} = {
userId => {
fieldType => 'user',
defaultValue => undef,
},
transactionItemId => {
fieldType => 'guid',
defaultValue => undef,
},
adId => {
fieldType => 'guid',
defaultValue => undef,
},
clicksPurchased => {
fieldType => 'integer',
defaultValue => undef,
},
impressionsPurchased => {
fieldType => 'integer',
defaultValue => undef,
},
dateOfPurchase => {
fieldType => 'date',
defaultValue => undef,
},
storedImage => {
fieldType => 'guid',
defaultValue => undef,
},
isDeleted => {
fieldType => 'yesNo',
defaultValue => 0,
},
};
return $definition;
}
1;

View file

@ -317,8 +317,6 @@ sub exportAsHtml {
# tell the user which asset we're exporting.
unless ($quiet) {
my $message = sprintf $i18n->get('exporting page'), $fullPath;
$exportSession->var->end;
$exportSession->close;
$self->session->output->print($message);
}
@ -335,7 +333,7 @@ sub exportAsHtml {
# next, tell the asset that we're exporting, so that it can export any
# of its collateral or other extra data.
eval { $asset->exportAssetCollateral($asset->exportGetUrlAsPath, $args) };
eval { $asset->exportAssetCollateral($asset->exportGetUrlAsPath, $args, $session) };
if($@) {
$returnCode = 0;
$message = $@;
@ -389,7 +387,7 @@ sub exportAsHtml {
#-------------------------------------------------------------------
=head2 exportAssetCollateral ( basePath, params )
=head2 exportAssetCollateral ( basePath, params, [ session ] )
Plug in point for complicated assets (like the CS, the Calendar) to manage
exporting their collateral data like other views, children threads and posts,
@ -410,6 +408,10 @@ particular asset.
A hashref with the quiet, userId, depth, and indexFileName parameters from
L</exportAsHtml>.
=head3 session
The session doing the full export. Can be used to report status messages.
=cut
sub exportAssetCollateral {
@ -541,7 +543,7 @@ sub exportGetUrlAsPath {
my $fileTypes = $config->get('exportBinaryExtensions');
# get the asset's URL as a URI::URL object for easy parsing of components
my $url = URI::URL->new($config->get("sitename")->[0] . $self->getUrl);
my $url = URI::URL->new($self->session->url->getSiteURL . $self->getUrl);
my @pathComponents = $url->path_components;
shift @pathComponents; # first item is the empty string
my $filename = pop @pathComponents;

View file

@ -829,7 +829,6 @@ sub setParent {
return 0 if ($newParent->getId eq $self->get("parentId")); # don't move it to where it already is
return 0 if ($newParent->getId eq $self->getId); # don't move it to itself
my $oldLineage = $self->get("lineage");
return 0 unless $newParent->canEdit;
my $lineage = $newParent->get("lineage").$newParent->getNextChildRank;
return 0 if ($lineage =~ m/^$oldLineage/); # can't move it to its own child
$self->session->db->beginTransaction;

View file

@ -125,6 +125,15 @@ Imports the data exported by the exportAssetData method. If the asset already ex
A hash reference containing the exported data.
=head3 options
A hash reference of options to change how the import works
=head4 inheritPermissions
Forces the all assets in the package to inherit ownerUserId, groupIdView and groupIdEdit
from the asset where it is deployed.
=cut
sub importAssetData {
@ -212,7 +221,7 @@ sub importAssetCollateralData {
#-------------------------------------------------------------------
=head2 importPackage ( storageLocation )
=head2 importPackage ( storageLocation, options )
Imports the data from a webgui package file.
@ -220,6 +229,10 @@ Imports the data from a webgui package file.
A reference to a WebGUI::Storage object that contains a webgui package file.
=head3 options
A hashref of options that are passed onto importAssetData.
=cut
sub importPackage {

View file

@ -298,11 +298,14 @@ sub createAccountSave {
$self->saveParams($userId,$self->authMethod,$properties);
if ($self->getSetting("sendWelcomeMessage")){
my $authInfo = "\n\n".$i18n->get(50).": ".$username;
$authInfo .= "\n".$i18n->get(51).": ".$password if($password);
$authInfo .= "\n\n";
WebGUI::Inbox->new($self->session)->addMessage({
message => $self->getSetting("welcomeMessage").$authInfo,
my $var;
$var->{welcomeMessage} = $self->getSetting("welcomeMessage");
$var->{newUser_username} = $username;
$var->{newUser_password} = $password;
my $message = WebGUI::Asset::Template->new($self->session,$self->getSetting('welcomeMessageTemplate'))->process($var);
WebGUI::Macro::process($self->session,\$message);
WebGUI::Inbox->new($self->session)->addMessage({
message => $message,
subject => $i18n->get(870),
userId => $self->userId,
status => 'completed',

View file

@ -273,10 +273,13 @@ sub createAccountSave {
to => $profile->{email},
subject => $i18n->get('email address validation email subject','AuthWebGUI')
});
$mail->addText(
$i18n->get('email address validation email body','AuthWebGUI') . "\n\n"
. $session->url->page("op=auth;method=validateEmail;key=".$key, 'full') . "\n\n"
);
my $var;
$var->{newUser_username} = $username;
$var->{activationUrl} = $session->url->page("op=auth;method=validateEmail;key=".$key, 'full');
my $text =
WebGUI::Asset::Template->new($self->session,$self->getSetting('accountActivationTemplate'))->process($var);
WebGUI::Macro::process($self->session,\$text);
$mail->addText($text);
$mail->addFooter;
$mail->send;
$self->user->status("Deactivated");
@ -494,7 +497,7 @@ sub editUserSettingsForm {
-label => $i18n->get(868,'WebGUI'),
-hoverHelp => $i18n->get('868 help','WebGUI'),
);
$f->textarea(
$f->HTMLArea(
-name => "webguiWelcomeMessage",
-value => $self->session->setting->get("webguiWelcomeMessage"),
-label => $i18n->get(869,'WebGUI'),
@ -574,7 +577,21 @@ sub editUserSettingsForm {
-label => $i18n->get("password recovery template"),
-hoverHelp => $i18n->get("password recovery template help")
);
return $f->printRowsOnly;
$f->template(
-name => "webguiWelcomeMessageTemplate",
-value => $self->session->setting->get("webguiWelcomeMessageTemplate"),
-namespace => "Auth/WebGUI/Welcome",
-label => $i18n->get("welcome message template"),
-hoverHelp => $i18n->get("welcome message template help")
);
$f->template(
-name => "webguiAccountActivationTemplate",
-value => $self->session->setting->get("webguiAccountActivationTemplate"),
-namespace => "Auth/WebGUI/Activation",
-label => $i18n->get("account activation template"),
-hoverHelp => $i18n->get("account activation template help")
);
return $f->printRowsOnly;
}
#-------------------------------------------------------------------
@ -625,6 +642,8 @@ sub editUserSettingsFormSave {
$s->set("webguiExpiredPasswordTemplate", $f->process("webguiExpiredPasswordTemplate","template"));
$s->set("webguiLoginTemplate", $f->process("webguiLoginTemplate","template"));
$s->set("webguiPasswordRecoveryTemplate", $f->process("webguiPasswordRecoveryTemplate","template"));
$s->set("webguiWelcomeMessageTemplate", $f->process("webguiWelcomeMessageTemplate","template"));
$s->set("webguiAccountActivationTemplate", $f->process("webguiAccountActivationTemplate","template"));
if (@errors) {
return \@errors;

View file

@ -137,7 +137,7 @@ sub getFolder {
#-------------------------------------------------------------------
=head2 getNamepsaceRoot ( )
=head2 getNamespaceRoot ( )
Figures out what the cache root for this namespace should be. A class method.

View file

@ -88,6 +88,17 @@ sub handler {
}
#-------------------------------------------------------------------
=head2 formatXML ( content )
Escape XML entities, &, <, >, ' and ".
=head3 content
The content that will have XML entities escaped.
=cut
sub formatXML {
my $content = shift;
$content =~ s/&/&amp;/g;

190
lib/WebGUI/Form/AdSpace.pm Normal file
View file

@ -0,0 +1,190 @@
package WebGUI::Form::AdSpace;
=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 base 'WebGUI::Form::SelectList';
use WebGUI::International;
use WebGUI::SQL;
=head1 NAME
Package WebGUI::Form::AdSpace
=head1 DESCRIPTION
Creates a group chooser field for AdSpace values.
=head1 SEE ALSO
This is a subclass of WebGUI::Form::SelectList.
=head1 METHODS
The following methods are specifically available from this class. Check the superclass for additional methods.
=cut
#-------------------------------------------------------------------
=head2 areOptionsSettable ( )
Returns 0.
=cut
sub areOptionsSettable {
return 0;
}
#-------------------------------------------------------------------
=head2 definition ( [ additionalTerms ] )
See the super class for additional details.
=head3 additionalTerms
The following additional parameters have been added via this sub class.
=head4 size
How many rows should be displayed at once? Defaults to 1.
=head4 defaultValue
This will be used if no value is specified. Should be passed as an array reference. Defaults to 1.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift || [];
push(@{$definition}, {
size=>{
defaultValue=>1
},
});
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getDatabaseFieldType ( )
Returns "CHAR(22) BINARY".
=cut
sub getDatabaseFieldType {
return "CHAR(22) BINARY";
}
#-------------------------------------------------------------------
=head2 getName ( session )
Returns the human readable name of this control.
=cut
sub getName {
my ($self, $session) = @_;
return WebGUI::International->new($session, 'WebGUI')->get('Ad Space control name');
}
#-------------------------------------------------------------------
=head2 getValueAsHtml ( )
Formats as a name.
=cut
sub getValueAsHtml {
my $self = shift;
my $item = WebGUI::AdSpace->new($self->session, $self->getOriginalValue);
if (defined $item) {
return $item->name;
}
return undef;
}
#-------------------------------------------------------------------
=head2 isDynamicCompatible ( )
A class method that returns a boolean indicating whether this control is compatible with the DynamicField control.
=cut
sub isDynamicCompatible {
return 1;
}
#-------------------------------------------------------------------
=head2 toHtml ( )
Returns a group pull-down field. A group pull down provides a select list that provides name value pairs for all the groups in the WebGUI system.
=cut
sub toHtml {
my $self = shift;
my $options = { map { $_->getId => $_->get('name') } ( @{ WebGUI::AdSpace->getAdSpaces($self->session) } ) };
$self->set('defaultValue', ( keys %{$options} )[0] );
$self->set('options', $options );
return $self->SUPER::toHtml();
}
#-------------------------------------------------------------------
=head2 toHtmlAsHidden ( )
Creates a series of hidden fields representing the data in the list.
=cut
sub toHtmlAsHidden {
my $self = shift;
my $options = { map { $_->getId => $_->get('name') } ( @{ WebGUI::AdSpace->getAdSpaces($self->session) } ) };
$self->set('defaultValue', ( keys %{$options} )[0] );
$self->set('options', $options );
return $self->SUPER::toHtmlAsHidden();
}
#-------------------------------------------------------------------
=head2 toHtmlWithWrapper ( )
Renders the form field to HTML as a table row complete with labels, subtext, hoverhelp, etc. Also adds a manage icon next to the field if the current user is in the admins group.
=cut
sub toHtmlWithWrapper {
my $self = shift;
if ($self->session->user->isAdmin) {
my $subtext = $self->session->icon->manage("op=manageAdSpaces");
$self->set("subtext",$subtext . $self->get("subtext"));
}
return $self->SUPER::toHtmlWithWrapper;
}
1;

View file

@ -182,6 +182,12 @@ sub toHtml {
return $self->SUPER::toHtml.'<p style="display:inline;vertical-align:middle;"><img src="'.$storage->getUrl($filename).'" style="border-style:none;vertical-align:middle;" alt="captcha" /></p>';
}
=head2 getErrorMessage ( )
Returns an internationalized error message based on which kind of captcha is being used.
=cut
sub getErrorMessage {
my $self = shift;
my $session = $self->session;

View file

@ -137,7 +137,7 @@ sub toHtml {
my $output = "";
# Do our superclass's job
my $value = $self->fixMacros($self->fixTags($self->fixSpecialCharacters($self->getOriginalValue)));
my $value = $self->fixMacros($self->fixTags($self->fixSpecialCharacters(scalar $self->getOriginalValue)));
my $width = $self->get('width') || 400;
my $height = $self->get('height') || 150;
my ($style, $url) = $self->session->quick(qw(style url));

View file

@ -196,6 +196,9 @@ sub definition {
idPrefix=>{
defaultValue=>undef
},
allowEmpty=>{
defaultValue => 0,
},
});
return $definition;
}
@ -686,7 +689,7 @@ Renders the form field to HTML as a hidden field rather than whatever field type
sub toHtmlAsHidden {
my $self = shift;
return '<input type="hidden" name="'.$self->get("name").'" value="'.
$self->fixQuotes($self->fixMacros($self->fixSpecialCharacters($self->getOriginalValue()))).'" />'."\n";
$self->fixQuotes($self->fixMacros($self->fixSpecialCharacters(scalar $self->getOriginalValue()))).'" />'."\n";
}
#-------------------------------------------------------------------

173
lib/WebGUI/Form/Keywords.pm Normal file
View file

@ -0,0 +1,173 @@
package WebGUI::Form::Keywords;
=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 base 'WebGUI::Form::Text';
use WebGUI::International;
use JSON ();
use WebGUI::Keyword;
=head1 NAME
Package WebGUI::Form::Keywords
=head1 DESCRIPTION
Creates a keywords chooser field with multiple select and autocomplete.
=head1 SEE ALSO
This is a subclass of WebGUI::Form::SelectList.
=head1 METHODS
The following methods are specifically available from this class. Check the superclass for additional methods.
=cut
#-------------------------------------------------------------------
=head2 getDatabaseFieldType ( )
Returns "CHAR(22) BINARY".
=cut
sub getDatabaseFieldType {
return "CHAR(255)";
}
#-------------------------------------------------------------------
=head2 getName ( session )
Returns the human readable name of this control.
=cut
sub getName {
my ($self, $session) = @_;
return WebGUI::International->new($session, 'Asset')->get('keywords');
}
#-------------------------------------------------------------------
=head2 isDynamicCompatible ( )
A class method that returns a boolean indicating whether this control is compatible with the DynamicField control.
=cut
sub isDynamicCompatible {
return 1;
}
#-------------------------------------------------------------------
=head2 toHtml ( )
Returns a keyword pull-down field. A keyword pull down provides a select list that provides name value pairs for all the keywords in the WebGUI system.
=cut
sub toHtml {
my $self = shift;
my $session = $self->session;
my $style = $session->style;
my $url = $session->url;
$style->setLink($url->extras("yui/build/autocomplete/assets/skins/sam/autocomplete.css"), {rel=>"stylesheet", type=>"text/css"});
$style->setScript($url->extras("yui/build/yahoo-dom-event/yahoo-dom-event.js"), {type=>"text/javascript"});
$style->setScript($url->extras("yui/build/datasource/datasource-min.js"), {type=>"text/javascript"});
$style->setScript($url->extras("yui/build/autocomplete/autocomplete-min.js"), {type=>"text/javascript"});
$style->setRawHeadTags('<style type="text/css">.yui-skin-sam.webgui-keywords-autocomplete .yui-ac-input { position: static; width: auto }</style>');
my $name = $self->generateIdParameter($self->get('name'));
my $autocompleteDiv = $self->privateName('autocomplete');
my $pageUrl = $url->page;
my $output
= '<div class="yui-skin-sam webgui-keywords-autocomplete"><div>' . $self->SUPER::toHtml
. '<div id="' . $autocompleteDiv . '"></div>'
. '<script type="text/javascript">' . <<"END_SCRIPT" . '</script></div></div>';
(function() {
var oDS = new YAHOO.util.XHRDataSource('$pageUrl');
oDS.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;
oDS.responseSchema = {
resultsList : "keywords",
};
var oAC = new YAHOO.widget.AutoComplete("$name", "$autocompleteDiv", oDS);
oAC.queryDelay = 0.5;
oAC.maxResultsDisplayed = 20;
oAC.minQueryLength = 3;
oAC.delimChar = [','];
oAC.generateRequest = function(sQuery) {
return "?op=formHelper;class=Keywords;sub=searchAsJSON;search=" + sQuery ;
};
})();
END_SCRIPT
return $output;
}
sub www_searchAsJSON {
my $session = shift;
my $search = $session->form->param('search');
my $keyword = WebGUI::Keyword->new($session);
my $keywords = $keyword->findKeywords({search => $search, limit => 20});
$session->http->setMimeType('application/json');
return JSON::to_json({keywords => $keywords});
}
sub getDefaultValue {
my $self = shift;
return _formatKeywordsAsWanted($self->SUPER::getDefaultValue(@_));
}
sub getOriginalValue {
my $self = shift;
return _formatKeywordsAsWanted($self->SUPER::getOriginalValue(@_));
}
sub getValue {
my $self = shift;
return _formatKeywordsAsWanted($self->SUPER::getValue(@_));
}
sub _formatKeywordsAsWanted {
my @keywords;
if (@_ == 1 && ref $_[0] eq 'ARRAY') {
@keywords = @{ $_[0] };
}
else {
for my $param (@_) {
for my $keyword (split /,/, $param) {
$keyword =~ s/^\s+//;
$keyword =~ s/\s+$//;
push @keywords, $keyword;
}
}
}
if (wantarray) {
return @keywords;
}
return join(', ', @keywords);
}
1;

View file

@ -220,7 +220,7 @@ sub getValue {
@values = $self->session->form->param($self->get("name"));
}
}
if (scalar @values < 1) {
if (scalar @values < 1 && ! $self->get('allowEmpty')) {
@values = $self->getDefaultValue;
}
return wantarray ? @values : join("\n",@values);
@ -262,18 +262,17 @@ Returns the either the "value" ore "defaultValue" passed in to the object in tha
sub getOriginalValue {
my $self = shift;
my @values = ();
foreach my $value ($self->get("value")) {
if (scalar @values < 1 && defined $value) {
if (ref $value eq "ARRAY") {
@values = @{$value};
}
else {
$value =~ s/\r//g;
@values = split "\n", $value;
}
my $value = $self->get("value");
if (defined $value) {
if (ref $value eq "ARRAY") {
@values = @{$value};
}
else {
$value =~ s/\r//g;
@values = split "\n", $value;
}
}
if (@values) {
if (@values || ($self->get('allowEmpty') && defined $value) ) {
return wantarray ? @values : join("\n",@values);
}

View file

@ -116,7 +116,7 @@ sub isDynamicCompatible {
#----------------------------------------------------------------------------
=head2 new
=head2 getOptions
Create a new WebGUI::Form::SelectRichEditor object and populate it with all
the available Rich Text Editor assets.

View file

@ -85,7 +85,7 @@ Renders the form field to HTML as a table row. The row is not displayed because
sub toHtmlWithWrapper {
my $self = shift;
my $value = $self->fixMacros($self->fixQuotes($self->fixSpecialCharacters($self->getOriginalValue))) || '';
my $value = $self->fixMacros($self->fixQuotes($self->fixSpecialCharacters(scalar $self->getOriginalValue))) || '';
if ($value) {
my $manageButton = $self->session->icon->manage("op=editGroup;gid=".$value);
$self->set("subtext",$manageButton . $self->get("subtext"));

View file

@ -125,7 +125,7 @@ Renders an input tag of type text.
sub toHtml {
my $self = shift;
my $value = $self->fixMacros($self->fixQuotes($self->fixSpecialCharacters($self->getOriginalValue)));
my $value = $self->fixMacros($self->fixQuotes($self->fixSpecialCharacters(scalar $self->getOriginalValue)));
return '<input id="'.$self->get('id').'" type="text" name="'.$self->get("name").'" value="'.$value.'" size="'.$self->get("size").'" maxlength="'.$self->get("maxlength").'" '.$self->get("extras").' />';
}

View file

@ -139,7 +139,7 @@ Renders an input tag of type text.
sub toHtml {
my $self = shift;
my $value = $self->fixMacros($self->fixTags($self->fixSpecialCharacters($self->getOriginalValue)));
my $value = $self->fixMacros($self->fixTags($self->fixSpecialCharacters(scalar $self->getOriginalValue)));
my $width = $self->get('width') || 400;
my $height = $self->get('height') || 150;
my ($style, $url) = $self->session->quick(qw(style url));
@ -195,6 +195,14 @@ sub toHtml {
return $out;
}
#-------------------------------------------------------------------
=head2 getValueAsHtml
Returns the form value as text, encoding HTML entities.
=cut
sub getValueAsHtml {
my $self = shift;
my $value = $self->SUPER::getValueAsHtml(@_);

View file

@ -442,6 +442,18 @@ sub splitTag {
return @result if wantarray;
return $result[0];
}
#-------------------------------------------------------------------
=head2 WebGUI::HTML::splitHeadBody($html);
splits an block of HTML into a HEAD and a BODY section
=head3 html
The block of HTML text that will be disected
=cut
sub splitHeadBody {
my $html = shift;

View file

@ -0,0 +1,29 @@
package WebGUI::Help::Asset_Carousel;
use strict;
our $HELP = {
'search template' => {
title => 'carousel template help title',
body => '',
isa => [
{ namespace => "Asset_Wobject",
tag => "wobject template variables",
},
],
variables => [
{ 'name' => 'item_loop',
'variables' => [
{ 'name' => 'text' },
{ 'name' => 'itemId'},
{ 'name' => 'sequenceNumber'},
],
}
],
related => [],
},
};
1;

View file

@ -53,7 +53,7 @@ our $HELP = {
{ 'name' => 'page.isChild' },
{ 'name' => 'page.isParent' },
{ 'name' => 'page.isCurrent' },
{ 'name' => 'page.isDescendent' },
{ 'name' => 'page.isDescendant' },
{ 'name' => 'page.isAncestor' },
{ 'name' => 'page.inBranchRoot' },
{ 'name' => 'page.isSibling' },

View file

@ -136,11 +136,7 @@ our $HELP = {
private => 1,
title => 'post asset variables title',
body => '',
isa => [
{ tag => 'asset template asset variables',
namespace => 'Asset'
},
],
isa => [ ],
variables => [
{ 'name' => 'storageId' },
{ 'name' => 'threadId' },

View file

@ -1,53 +0,0 @@
package WebGUI::Help::Asset_RSSFromParent;
use strict;
our $HELP = {
'rss from parent' => {
title => 'rss from parent title',
body => 'rss from parent body',
# use the following to inherit stuff other help entries
isa => [
{ tag => 'template variables',
namespace => 'Asset_Template'
},
],
fields => [ #This array is used to list hover help for form fields.
],
variables => [
{ 'name' => 'title',
'description' => 'title.parent'
},
{ 'name' => 'link',
'description' => 'title.parent'
},
{ 'name' => 'description',
'description' => 'description.parent'
},
{ 'name' => 'generator' },
{ 'name' => 'lastBuildDate' },
{ 'name' => 'webMaster' },
{ 'name' => 'docs' },
{ 'name' => 'item_loop',
variables => [
{ 'name' => 'title',
'description' => 'title.item'
},
{ 'name' => 'link',
'description' => 'title.item'
},
{ 'name' => 'description',
'description' => 'description.item'
},
{ 'name' => 'guid' },
{ 'name' => 'pubDate' },
]
},
],
related => [ ##This lists other help articles that are related to this one
],
},
};
1; ##All perl modules must return true

View file

@ -21,8 +21,11 @@ our $HELP = {
variables => [
{ name => "shelves" , required=>1},
{ name => "products" , required=>1, variables => [
{ name => "url",
description => 'product_url', },
{ name => "thumbnailUrl" },
{ name => "price" },
{ name => "addToCartForm" },
],
},
{ name => "templateId", description=>"shelf template help" },

View file

@ -26,6 +26,8 @@ our $HELP = {
{ 'name' => 'thing_deleteUrl' },
{ 'name' => 'thing_searchUrl' },
{ 'name' => 'thing_addUrl' },
{ 'name' => 'thing_copyUrl' },
{ 'name' => 'thing_copyIcon' },
]
},
],

View file

@ -163,6 +163,28 @@ our $HELP = {
related => [],
},
'webgui welcome message template' => {
title => 'welcome message template title',
body => '',
variables => [
{ 'name' => 'welcomeMessage' },
{ 'name' => 'newUser_username' },
{ 'name' => 'newUser_password' },
],
fields => [],
related => []
},
'account activation template' => {
title => 'account activation template title',
body => '',
variables => [
{ 'name' => 'newUser_username' },
{ 'name' => 'activationUrl' },
],
fields => [],
related => []
},
};
1;

View file

@ -163,14 +163,9 @@ sub create {
$preface = sprintf($i18n->get('from user preface'), $fromUser->username);
}
my $msg = (defined $properties->{emailMessage}) ? $properties->{emailMessage} : $self->{_properties}{message};
if ($msg =~ m/\<.*\>/) {
$msg = '<p>' . $preface . '</p><br />'.$msg if($preface ne "");
$mail->addHtml($msg);
} else {
$msg = $preface."\n\n".$msg if($preface ne "");
$mail->addText($msg);
}
$mail->addFooter;
$msg = '<p>' . $preface . '</p><br />'.$msg if($preface ne "");
$mail->addHtml($msg);
$mail->addFooter;
$mail->queue;
}
$self->{_session} = $session;

View file

@ -88,6 +88,67 @@ sub deleteKeyword {
$self->session->db->write("delete from assetKeyword where keyword=?", [$options->{keyword}]);
}
#-------------------------------------------------------------------
=head2 findKeywords ( $options )
Find keywords.
=head3 $options
A hashref of options to change the behavior of the method.
=head4 asset
Find all keywords for all assets below an asset, providing a WebGUI::Asset object.
=head4 assetId
Find all keywords for all assets below an asset, providing an assetId.
=head4 search
Find all keywords using the SQL clause LIKE. This can be used in tandem with asset or assetId.
=head4 limit
Limit the number of keywords that are returned.
=cut
sub findKeywords {
my $self = shift;
my $options = shift;
my $sql = 'SELECT keyword FROM assetKeyword';
my @where;
my @placeholders;
my $parentAsset;
if ($options->{asset}) {
$parentAsset = $options->{asset};
}
if ($options->{assetId}) {
$parentAsset = WebGUI::Asset->new($self->session, $options->{assetId});
}
if ($parentAsset) {
$sql .= ' INNER JOIN asset USING (assetId)';
push @where, 'lineage LIKE ?';
push @placeholders, $parentAsset->get('lineage') . '%';
}
if ($options->{search}) {
push @where, 'keyword LIKE ?';
push @placeholders, '%' . $options->{search} . '%';
}
if (@where) {
$sql .= ' WHERE ' . join(' AND ', @where);
}
$sql .= ' GROUP BY keyword';
if ($options->{limit}) {
$sql .= ' LIMIT ' . $options->{limit};
}
my $keywords = $self->session->db->buildArrayRef($sql, \@placeholders);
return $keywords;
}
#-------------------------------------------------------------------
@ -123,14 +184,32 @@ sub generateCloud {
my $self = shift;
my $options = shift;
my $display = $options->{displayAsset} || $options->{startAsset};
my $sth = $self->session->db->read("select count(*) as keywordTotal, keyword from assetKeyword
left join asset using (assetId) where lineage like ? group by keyword order by keywordTotal desc limit 50",
[ $options->{startAsset}->get("lineage").'%' ]);
my $includeKeywords = $options->{includeOnlyKeywords};
my $maxKeywords = $options->{maxKeywords} || 50;
if ($maxKeywords > 100) {
$maxKeywords = 100;
}
my $urlCallback = $options->{urlCallback};
my $extraWhere = '';
my @extraPlaceholders;
if ($includeKeywords) {
$extraWhere .= ' AND keyword IN (' . join(',', ('?') x @{$includeKeywords}) . ')';
push @extraPlaceholders, @{$includeKeywords};
}
my $sth = $self->session->db->read("SELECT COUNT(*) as keywordTotal, keyword FROM assetKeyword
LEFT JOIN asset USING (assetId) WHERE lineage LIKE ? $extraWhere
GROUP BY keyword ORDER BY keywordTotal DESC LIMIT ?",
[ $options->{startAsset}->get("lineage").'%', @extraPlaceholders, $maxKeywords ]);
my $cloud = HTML::TagCloud->new(levels=>$options->{cloudLevels} || 24);
while (my ($count, $keyword) = $sth->array) {
$cloud->add($keyword, $display->getUrl("func=".$options->{displayFunc}.";keyword=".$keyword), $count);
my $url
= $urlCallback ? $display->$urlCallback($keyword)
: $options->{displayFunc} ? $display->getUrl("func=".$options->{displayFunc}.";keyword=".$keyword)
: $display->getUrl("keyword=".$keyword)
;
$cloud->add($keyword, $url, $count);
}
return $cloud->html_and_css($options->{maxKeywords});
return $cloud->html_and_css($maxKeywords);
}
#-------------------------------------------------------------------
@ -152,13 +231,14 @@ A boolean, that if set to 1 will return the keywords as an array reference rathe
sub getKeywordsForAsset {
my ($self, $options) = @_;
my @keywords = $self->session->db->buildArray("select keyword from assetKeyword where assetId=?",
[$options->{asset}->getId]);
my $assetId = $options->{asset} ? $options->{asset}->getId : $options->{assetId};
my $keywords = $self->session->db->buildArrayRef("select keyword from assetKeyword where assetId=?",
[$assetId]);
if ($options->{asArrayRef}) {
return \@keywords;
return $keywords;
}
else {
return join(" ", map({ (m/\s/) ? '"' . $_ . '"' : $_ } @keywords));
return join(', ', @$keywords);
}
}
@ -321,9 +401,9 @@ Either a string of space-separated keywords, or an array reference of keywords t
sub setKeywordsForAsset {
my $self = shift;
my $options = shift;
my $keywords = [];
my $keywords;
if (ref $options->{keywords} eq "ARRAY") {
$keywords = $options->{keywords};
$keywords = $options->{keywords};
}
else {
$keywords = string2list($options->{keywords});
@ -339,7 +419,7 @@ sub setKeywordsForAsset {
next
if $found_keywords{$keyword};
$found_keywords{$keyword}++;
$sth->execute([$assetId, lc($keyword)]);
$sth->execute([$assetId, $keyword]);
}
}
}
@ -352,35 +432,18 @@ Returns an array reference of phrases.
=head3 string
A scalar containing space separated phrases.
A scalar containing comma separated phrases.
=cut
sub string2list {
my $text = shift;
return if (ref $text);
my @words = ();
my $word = '';
my $errorFlag = 0;
while ( defined $text and length $text and not $errorFlag) {
if ($text =~ s/\A(?: ([^\"\s\\]+) | \\(.) )//mx) {
$word .= $1;
}
elsif ($text =~ s/\A"((?:[^\"\\]|\\.)*)"//mx) {
$word .= $1;
}
elsif ($text =~ s/\A\s+//m){
push(@words, $word);
$word = '';
}
elsif ($text =~ s/\A"//) {
$errorFlag = 1;
}
else {
$errorFlag = 1;
}
my @words = split /,/, $text;
for my $word (@words) {
$word =~ s/^\s+//;
$word =~ s/\s+$//;
}
push(@words, $word);
return \@words;
}

Some files were not shown because too many files have changed in this diff Show more