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

@ -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;
}

View file

@ -58,7 +58,7 @@ sub www_switchOffAdmin {
#-------------------------------------------------------------------
=head2 www_adminConsole ( )
=head2 www_switchOnAdmin ( )
If the current user is in the Turn On Admin Group, then allow them to turn on Admin mode.

View file

@ -451,6 +451,17 @@ sub www_commitVersionTagConfirm {
}
#-------------------------------------------------------------------
=head2 www_leaveVersionTag ( session )
Clears the current working version tag, and returns the user to www_manageVersions.
=head3 session
A reference to the current session.
=cut
sub www_leaveVersionTag {
my $session = shift;
WebGUI::VersionTag->getWorking($session)->clearWorking;

View file

@ -173,8 +173,11 @@ returns true is the client/agent is a spider/indexer or some other non-human int
sub requestNotViewed {
my $self = shift;
return $self->clientIsSpider()
|| $self->callerIsSearchSite();
return $self->clientIsSpider();
# || $self->callerIsSearchSite(); # this part is currently left out because
# it has minimal effect and does not manage
# IPv6 addresses. it may be useful in the
# future though
}

View file

@ -87,16 +87,25 @@ sub DESTROY {
#-------------------------------------------------------------------
=head2 get ( )
=head2 get ( $param )
Returns a hash reference containing all the settings.
=head3 $param
If $param is defined, then it will return only the setting for that param.
=cut
sub get {
my $self = shift;
my $param = shift;
return $self->{_settings}{$param};
my $self = shift;
my $param = shift;
if (defined $param) {
return $self->{_settings}{$param};
}
else {
return $self->{_settings};
}
}

View file

@ -152,6 +152,13 @@ sub www_editSettings {
label => $i18n->get('who is a cashier'),
hoverHelp => $i18n->get('who is a cashier help'),
);
$form->float(
name => 'shopCartCheckoutMinimum',
value => $setting->get('shopCartCheckoutMinimum'),
defaultValue=> '0.00',
label => $i18n->get('cart checkout minimum'),
hoverHelp => $i18n->get('cart checkout minimum help'),
);
$form->template(
name => "shopCartTemplateId",
value => $setting->get("shopCartTemplateId"),
@ -203,13 +210,21 @@ sub www_editSettingsSave {
my $self = shift;
return $self->session->privilege->adminOnly() unless ($self->session->user->isAdmin);
my ($setting, $form) = $self->session->quick(qw(setting form));
# Save shop templates
foreach my $template (qw(shopMyPurchasesDetailTemplateId shopMyPurchasesTemplateId
shopCartTemplateId shopAddressBookTemplateId shopAddressTemplateId)) {
$setting->set($template, $form->get($template, "template"));
}
# Save group settings
foreach my $group (qw(groupIdCashier groupIdAdminCommerce)) {
$setting->set($group, $form->get($group, "group"));
}
# Save mininmum cart checkout
$setting->set( 'shopCartCheckoutMinimum', $form->get( 'shopCartCheckoutMinimum', 'float' ) );
return $self->www_editSettings();
}

View file

@ -175,7 +175,7 @@ sub create {
WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session.");
}
my $cartId = $session->id->generate;
$session->db->write('insert into cart (cartId, sessionId) values (?,?)', [$cartId, $session->getId]);
$session->db->write('insert into cart (cartId, sessionId, creationDate) values (?,?,UNIX_TIMESTAMP())', [$cartId, $session->getId]);
return $class->new($session, $cartId);
}
@ -498,21 +498,23 @@ Returns whether all the required properties of the the cart are set.
sub readyForCheckout {
my $self = shift;
# Check if the shipping address is set and correct
my $address = eval{$self->getShippingAddress};
return 0 if WebGUI::Error->caught;
# Check if the ship driver is chosen and existant
my $ship = eval {$self->getShipper};
return 0 if WebGUI::Error->caught;
# Check if the cart has items
return 0 unless scalar @{ $self->getItems };
# fail if there are multiple recurring items or if
return 0 if ($self->hasMixedItems);
# Check minimum cart checkout requirement
my $requiredAmount = $self->session->setting->get( 'shopCartCheckoutMinimum' );
if ( $requiredAmount > 0 ) {
return 0 if $self->calculateTotal < $requiredAmount;
}
# All checks passed so return true
return 1;
}
@ -559,6 +561,10 @@ The unique id of the configured shipping driver that will be used to ship these
The ID of a user being checked out, if they're being checked out by a cashier.
=head4 creationDate
The date the cart was created.
=cut
sub update {
@ -567,7 +573,7 @@ sub update {
WebGUI::Error::InvalidParam->throw(error=>"Need a properties hash ref.");
}
my $id = id $self;
foreach my $field (qw(shippingAddressId posUserId shipperId)) {
foreach my $field (qw(shippingAddressId posUserId shipperId creationDate)) {
$properties{$id}{$field} = (exists $newProperties->{$field}) ? $newProperties->{$field} : $properties{$id}{$field};
}
$self->session->db->setRow("cart","cartId",$properties{$id});
@ -801,6 +807,10 @@ sub www_view {
shipToButton => WebGUI::Form::submit($session, {value=>$i18n->get("ship to button"),
extras=>q|onclick="setCallbackForAddressChooser(this.form);"|}),
subtotalPrice => $self->formatCurrency($self->calculateSubtotal()),
minimumCartAmount => $session->setting->get( 'shopCartCheckoutMinimum' ) > 0
? sprintf( '%.2f', $session->setting->get( 'shopCartCheckoutMinimum' ) )
: 0
,
);
# get the shipping address
@ -846,10 +856,11 @@ sub www_view {
# calculate price adjusted for in-store credit
$var{totalPrice} = $var{subtotalPrice} + $var{shippingPrice} + $var{tax};
my $credit = WebGUI::Shop::Credit->new($session, $posUser->userId);
$var{inShopCreditAvailable} = $credit->getSum;
$var{inShopCreditDeduction} = $credit->calculateDeduction($var{totalPrice});
$var{totalPrice} = $self->formatCurrency($var{totalPrice} + $var{inShopCreditDeduction});
$var{ inShopCreditAvailable } = $credit->getSum;
$var{ inShopCreditDeduction } = $credit->calculateDeduction($var{totalPrice});
$var{ totalPrice } = $self->formatCurrency($var{totalPrice} + $var{inShopCreditDeduction});
$var{ readyForCheckout } = $self->readyForCheckout;
# render the cart
my $template = WebGUI::Asset::Template->new($session, $session->setting->get("shopCartTemplateId"));
return $session->style->userStyle($template->process(\%var));

View file

@ -175,7 +175,7 @@ sub getSku {
my ($self) = @_;
my $asset = '';
$asset = WebGUI::Asset->newByDynamicClass($self->cart->session, $self->get("assetId"));
$asset->applyOptions($self->get("options"));
$asset->applyOptions($self->get("options")) if $asset;
return $asset;
}
@ -229,7 +229,8 @@ Removes this item from the cart and calls $sku->onRemoveFromCart. See also delet
sub remove {
my $self = shift;
$self->getSku->onRemoveFromCart($self);
my $sku = $self->getSku;
$sku->onRemoveFromCart($self) if $sku;
return $self->delete;
}

View file

@ -226,6 +226,18 @@ Returns a reference to the current session.
=cut
#-------------------------------------------------------------------
=head2 www_addPaymentGateway ( $session )
Add a new payment gateway, based on the className form variable. It will throw
an error, WebGUI::Error::InvalidParram if no className is passed.
=head3 $session
A reference to the current session object.
=cut
sub www_addPaymentGateway {
my $self = shift;
my $session = $self->session;

View file

@ -9,7 +9,7 @@ use base qw/WebGUI::Shop::PayDriver/;
#-------------------------------------------------------------------
=head2 canCheckOutCart ( )
=head2 canCheckoutCart ( )
Returns whether the cart can be checked out by this plugin.
@ -85,12 +85,6 @@ sub processPayment {
#-------------------------------------------------------------------
sub www_displayStatus {
}
#-------------------------------------------------------------------
=head2 www_getCredentials ( [ addressId ] )
Displays the checkout form for this plugin.

View file

@ -119,6 +119,48 @@ sub getId {
#-------------------------------------------------------------------
=head2 getPayoutTotals ( )
Returns a hash ref, containing the payout details for this vendor. The keys in the hash are:
=head3 paid
The amount of money already transfered to the vendor.
=head3 scheduled
The amount of money scheduled to be transfered to the vendor.
=head3 notPaid
The amount of money that is yet to be scheduled for payment to the vendor.
=head3 total
The sum of these three values.
=cut
sub getPayoutTotals {
my $self = shift;
my %totals = $self->session->db->buildHash(
'select vendorPayoutStatus, sum(vendorPayoutAmount) as amount from transactionItem '
.'where vendorId=? group by vendorPayoutStatus ',
[ $self->getId ]
);
# Format the payout categories and calc the total those.
%totals =
map { lcfirst $_ => sprintf '%.2f', $totals{ $_ } }
qw( Paid Scheduled NotPaid );
$totals{ total } = sprintf '%.2f', sum values %totals;
return \%totals;
}
#-------------------------------------------------------------------
=head2 getVendors ( session, options )
Class method. Returns an array reference of WebGUI::Shop::Vendor objects.
@ -152,6 +194,26 @@ sub getVendors {
#-------------------------------------------------------------------
=head2 isVendorInfoComplete ( )
Returns a boolean indicating whether the payoutinformation entered by the vendor is complete.
=cut
sub isVendorInfoComplete {
my $self = shift;
my $complete =
defined $self->get( 'name' )
&& defined $self->get( 'userId' )
&& defined $self->get( 'preferredPaymentType' )
&& defined $self->get( 'paymentInformation' );
return $complete
}
#-------------------------------------------------------------------
=head2 new ( session, vendorId )
Constructor. Returns a WebGUI::Shop::Vendor object.
@ -407,47 +469,144 @@ sub www_manage {
return $console->render($output, $i18n->get("vendors"));
}
#-------------------------------------------------------------------
=head2 getPayoutTotals ( )
=head2 www_managePayouts ( )
Returns a hash ref, containing the payout details for this vendor. The keys in the hash are:
=head3 paid
The amount of money already transfered to the vendor.
=head3 scheduled
The amount of money scheduled to be transfered to the vendor.
=head3 notPaid
The amount of money that is yet to be scheduled for payment to the vendor.
=head3 total
The sum of these three values.
Displays the payout manager.
=cut
sub getPayoutTotals {
my $self = shift;
sub www_managePayouts {
my $class = shift;
my $session = shift;
my %totals = $self->session->db->buildHash(
'select vendorPayoutStatus, sum(vendorPayoutAmount) as amount from transactionItem '
.'where vendorId=? group by vendorPayoutStatus ',
[ $self->getId ]
);
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
# Load the required YUI stuff.
my $style = $session->style;
my $url = $session->url;
# Format the payout categories and calc the total those.
%totals =
map { lcfirst $_ => sprintf '%.2f', $totals{ $_ } }
qw( Paid Scheduled NotPaid );
$totals{ total } = sprintf '%.2f', sum values %totals;
$style->setLink($url->extras('yui/build/paginator/assets/skins/sam/paginator.css'), {type=>'text/css', rel=>'stylesheet'});
$style->setLink($url->extras('yui/build/datatable/assets/skins/sam/datatable.css'), {type=>'text/css', rel=>'stylesheet'});
$style->setLink($url->extras('yui/build/button/assets/skins/sam/button.css'), {type=>'text/css', rel=>'stylesheet'});
return \%totals;
$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/connection/connection-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/json/json-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/paginator/paginator-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/datasource/datasource.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/datatable/datatable-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('yui/build/button/button-min.js'), {type=>'text/javascript'});
$style->setScript($url->extras('VendorPayout/vendorPayout.js'), {type=>'text/javascript'});
# Add css for scheduled payout highlighting
$style->setRawHeadTags(<<CSS);
<style type="text/css">
.yui-skin-sam .yui-dt tr.scheduled,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-asc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-desc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-asc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-desc {
background-color : #080;
color : #fff;
}
</style>
CSS
my $output = q{<div id="vendorPayoutContainer" class="yui-skin-sam"></div>}
.q{<script type="text/javascript">var vp = new WebGUI.VendorPayout( 'vendorPayoutContainer' );</script>};
my $console = WebGUI::Shop::Admin->new($session)->getAdminConsole;
my $i18n = WebGUI::International->new($session, 'Shop');
return $console->render($output, $i18n->get('vendor payouts'));
}
#-------------------------------------------------------------------
=head2 www_payoutDataAsJSON ( )
Returns a JSON string containing paginated payout data for a specific vendor.
The following form params should be passed:
=head3 vendorId
The vendorId of the vendor you want the payout data for.
=head3 results
The number of results to be returned. Defaults to 100.
=head3 startIndex
The index of the record at which the payout data should start.
=cut
sub www_payoutDataAsJSON {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $vendorId = $session->form->process('vendorId');
my $startIndex = $session->form->process('startIndex');
my $rowsPerPage = $session->form->process('results') || 100;
my $pageNumber = int( $startIndex / $rowsPerPage ) + 1;
my $sql =
"select t1.* from transactionItem as t1 join transaction as t2 on t1.transactionId=t2.transactionId "
." where vendorId=? and vendorPayoutAmount > 0 and vendorPayoutStatus <> 'Paid' order by t2.orderNumber";
my $placeholders = [ $vendorId ];
my $paginator = WebGUI::Paginator->new( $session, '', $rowsPerPage, '', $pageNumber );
$paginator->setDataByQuery( $sql, undef, 0, $placeholders );
my $data = {
totalRecords => $paginator->getRowCount,
results => $paginator->getPageData,
};
$session->http->setMimeType( 'application/json' );
return JSON::to_json( $data );
}
#-------------------------------------------------------------------
=head2 www_setPayoutStatus ( )
Sets the vendorPayoutStatus flag for each transaction passed by the form param 'itemId'. The new status is passed
by the form param 'status'. Status can either be 'NotPaid' or 'Scheduled' and may only be applied on items that do
not have their vendorPayoutStatus set to 'Paid'.
Returns the status to which the item(s) are set.
=cut
sub www_setPayoutStatus {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my @itemIds = $session->form->process('itemId');
my $status = $session->form->process('status');
return "error: wrong status [$status]" unless isIn( $status, qw{ NotPaid Scheduled } );
foreach my $itemId (@itemIds) {
my $item = WebGUI::Shop::TransactionItem->newByDynamicTransaction( $session, $itemId );
return "error: invalid transactionItemId [$itemId]" unless $item;
return "error: cannot change status of a Paid item" if $item->get('vendorPayoutStatus') eq 'Paid';
$item->update({ vendorPayoutStatus => $status });
}
return $status;
}
#-------------------------------------------------------------------
@ -477,43 +636,14 @@ sub www_submitScheduledPayouts {
#-------------------------------------------------------------------
=head2 www_setPayoutStatus ( )
Sets the vendorPayoutStatus flag for each transaction passed by the form param 'itemId'. The new status is passed
by the form param 'status'. Status can either be 'NotPaid' or 'Scheduled' and may only be applied on items that do
not have their vendorPayoutStatus set to 'Paid'.
Returns the status to which the item(s) are set.
=cut
sub www_setPayoutStatus {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my @itemIds = $session->form->process('itemId');
my $status = $session->form->process('status');
return "error: wrong status [$status]" unless isIn( $status, qw{ NotPaid Scheduled } );
foreach my $itemId (@itemIds) {
my $item = WebGUI::Shop::TransactionItem->newByDynamicTransaction( $session, $itemId );
return "error: invalid transactionItemId [$itemId]" unless $item;
return "error: cannot change status of a Paid item" if $item->get('vendorPayoutStatus') eq 'Paid';
$item->update({ vendorPayoutStatus => $status });
}
return $status;
}
#-------------------------------------------------------------------
=head2 www_vendorTotalsAsJSON ( )
Returns a JSON string containing all vendors and their payout details. If a vendor id is passed through form param
'vendorId' only results for that vendor will be returned.
Returns a JSON string containing all vendors and their payout details. The following
form parameters can be passed:
=head3 vendorId
If passed, the results will include only the totals of this vendor.
=cut
@ -555,78 +685,5 @@ sub www_vendorTotalsAsJSON {
return JSON::to_json( { vendors => \@dataset } );
}
#-------------------------------------------------------------------
sub www_payoutDataAsJSON {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
my $vendorId = $session->form->process('vendorId');
my $startIndex = $session->form->process('startIndex');
my $rowsPerPage = $session->form->process('results') || 100;
my $pageNumber = int( $startIndex / $rowsPerPage ) + 1;
my $sql =
"select t1.* from transactionItem as t1 join transaction as t2 on t1.transactionId=t2.transactionId "
." where vendorId=? and vendorPayoutAmount > 0 and vendorPayoutStatus <> 'Paid' order by t2.orderNumber";
my $placeholders = [ $vendorId ];
my $paginator = WebGUI::Paginator->new( $session, '', $rowsPerPage, '', $pageNumber );
$paginator->setDataByQuery( $sql, undef, 0, $placeholders );
my $data = {
totalRecords => $paginator->getRowCount,
results => $paginator->getPageData,
};
$session->http->setMimeType( 'application/json' );
return JSON::to_json( $data );
}
#-------------------------------------------------------------------
sub www_managePayouts {
my $class = shift;
my $session = shift;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->adminOnly() unless ($admin->canManage);
# Load the required YUI stuff.
$session->style->setLink('/extras/yui/build/paginator/assets/skins/sam/paginator.css', {type=>'text/css', rel=>'stylesheet'});
$session->style->setLink('/extras/yui/build/datatable/assets/skins/sam/datatable.css', {type=>'text/css', rel=>'stylesheet'});
$session->style->setLink('/extras/yui/build/button/assets/skins/sam/button.css', {type=>'text/css', rel=>'stylesheet'});
$session->style->setScript('/extras/yui/build/yahoo-dom-event/yahoo-dom-event.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/element/element-beta-min.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/connection/connection-min.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/json/json-min.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/paginator/paginator-min.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/datasource/datasource.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/datatable/datatable-min.js', {type=>'text/javascript'});
$session->style->setScript('/extras/yui/build/button/button-min.js', {type=>'text/javascript'});
$session->style->setScript('/extras/VendorPayout/vendorPayout.js', {type=>'text/javascript'});
# Add css for scheduled payout highlighting
$session->style->setRawHeadTags(<<CSS);
<style type="text/css">
.yui-skin-sam .yui-dt tr.scheduled,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-asc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-desc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-asc,
.yui-skin-sam .yui-dt tr.scheduled td.yui-dt-desc {
background-color : #080;
color : #fff;
}
</style>
CSS
my $output = q{<div id="vendorPayoutContainer" class="yui-skin-sam"></div>}
.q{<script type="text/javascript">var vp = new WebGUI.VendorPayout( 'vendorPayoutContainer' );</script>};
my $console = WebGUI::Shop::Admin->new($session)->getAdminConsole;
return $console->render($output, 'Vendor payout'); #$i18n->get("vendors"));
}
1;

View file

@ -678,6 +678,32 @@ sub generateThumbnail {
#-------------------------------------------------------------------
=head2 getSize ( filename )
Returns width and height of image.
=head3 filename
The file to generate a thumbnail for.
=cut
sub getSize {
my $self = shift;
my $filename = shift;
my $image = Image::Magick->new;
my $error = $image->Read($self->getPath($filename));
if ($error) {
$self->session->errorHandler->error("Couldn't read image for size reading: ".$error);
return 0;
}
my ($x, $y) = $image->Get('width','height');
return($x, $y);
}
#-------------------------------------------------------------------
=head2 getErrorCount ( )
Returns the number of errors that have been generated on this object instance.
@ -1054,6 +1080,203 @@ sub renameFile {
#-------------------------------------------------------------------
=head2 crop ( filename [, width, height ] )
Resizes the specified image by the specified height and width. If either is omitted the iamge will be scaleed proportionately to the non-omitted one.
=head3 filename
The name of the file to resize.
=head3 width
The new width of the image in pixels.
=head3 height
The new height of the image in pixels.
=head3 x
The top of the image in pixels.
=head3 y
The top of the image in pixels.
=cut
# TODO: Make this take a hash reference with width, height, and density keys.
sub crop {
my $self = shift;
my $filename = shift;
my $width = shift;
my $height = shift;
my $x = shift;
my $y = shift;
unless (defined $filename) {
$self->session->errorHandler->error("Can't resize when you haven't specified a file.");
return 0;
}
unless ($self->isImage($filename)) {
$self->session->errorHandler->error("Can't resize something that's not an image.");
return 0;
}
unless ($width || $height || $x || $y) {
$self->session->errorHandler->error("Can't resize with no resizing parameters.");
return 0;
}
my $image = Image::Magick->new;
my $error = $image->Read($self->getPath($filename));
if ($error) {
$self->session->errorHandler->error("Couldn't read image for resizing: ".$error);
return 0;
}
# Next, resize dimensions
if ( $width || $height || $x || $y ) {
$self->session->errorHandler->info( "Resizing $filename to w:$width h:$height x:$x y:$y" );
$image->Crop( height => $height, width => $width, x => $x, y => $y );
}
# Write our changes to disk
$error = $image->Write($self->getPath($filename));
if ($error) {
$self->session->errorHandler->error("Couldn't resize image: ".$error);
return 0;
}
return 1;
}
#-------------------------------------------------------------------
=head2 annotate ( filename [ text ] )
Adds annotation text to the image.
=head3 filename
The name of the file to annotate.
=head3 text
Text to add.
=cut
sub annotate {
my $self = shift;
my $filename = shift;
my $asset = shift;
my $form = shift;
unless (defined $filename) {
$self->session->errorHandler->error("Can't rotate when you haven't specified a file.");
return 0;
}
unless ($self->isImage($filename)) {
$self->session->errorHandler->error("Can't rotate something that's not an image.");
return 0;
}
# unless ($annotate_text) {
# $self->session->errorHandler->error("Can't annotate with no text.");
# return 0;
# }
# unless ($annotate_top && $annotate_left && $annotate_width && $annotate_height) {
# $self->session->errorHandler->error("Can't annotate with no dimensions.");
# return 0;
# }
my $annotate = $asset->get('annotations');
my $save_annotate = "";
my @pieces = split(/\n/, $annotate);
for (my $i = 0; $i < $#pieces; $i += 3) {
my $top_left = $pieces[$i];
my $width_height = $pieces[$i + 1];
my $note = $pieces[$i + 2];
# warn("i: $i: ", $form->process("delAnnotate$i"));
next if $form->process("delAnnotate$i");
if ($save_annotate) {
$save_annotate .= "\n";
}
$save_annotate .= "$top_left\n$width_height\n$note";
}
my $annotate_text = $form->process("annotate_text");
my $annotate_top = $form->process("annotate_top");
my $annotate_left = $form->process("annotate_left");
my $annotate_width = $form->process("annotate_width");
my $annotate_height = $form->process("annotate_height");
# warn(qq(unless ($annotate_top && $annotate_left && $annotate_width && $annotate_height && $annotate_text !~ /^\s*$/)));
if (defined $annotate_top && defined $annotate_left && defined $annotate_width && defined $annotate_height && $annotate_text !~ /^\s*$/) {
if ($save_annotate) {
$save_annotate .= "\n";
}
# warn(qq($save_annotate .= "top: ${annotate_top}px; left: ${annotate_left}px;\nwidth: ${annotate_width}px; height: ${annotate_height}px;\n'$annotate_text'"));
$save_annotate .= "top: ${annotate_top}px; left: ${annotate_left}px;\nwidth: ${annotate_width}px; height: ${annotate_height}px;\n$annotate_text";
}
# warn($save_annotate);
$asset->update({ annotations => $save_annotate });
$save_annotate = $asset->get('annotations');
# warn($save_annotate);
return 1;
}
#-------------------------------------------------------------------
=head2 rotate ( filename [ degrees ] )
Rotates the image by the specified degrees.
=head3 filename
The name of the file to resize.
=head3 width
Number of degrees to rotate.
=cut
sub rotate {
my $self = shift;
my $filename = shift;
my $degree = shift || 0;
unless (defined $filename) {
$self->session->errorHandler->error("Can't rotate when you haven't specified a file.");
return 0;
}
unless ($self->isImage($filename)) {
$self->session->errorHandler->error("Can't rotate something that's not an image.");
return 0;
}
my $image = Image::Magick->new;
my $error = $image->Read($self->getPath($filename));
if ($error) {
$self->session->errorHandler->error("Couldn't read image for resizing: ".$error);
return 0;
}
$self->session->errorHandler->info( "Rotating $filename by $degree degrees" );
$image->Rotate( $degree );
# Write our changes to disk
$error = $image->Write($self->getPath($filename));
if ($error) {
$self->session->errorHandler->error("Couldn't rotate image: ".$error);
return 0;
}
return 1;
}
#-------------------------------------------------------------------
=head2 resize ( filename [, width, height ] )
Resizes the specified image by the specified height and width. If either is omitted the iamge will be scaleed proportionately to the non-omitted one.

View file

@ -161,9 +161,9 @@ sub acceptsFriendsRequests {
return 0 if($self->userId eq $user->userId); #Can't be your own friend (why would you want to be?)
my $me = WebGUI::Friends->new($session,$self);
my $friend = WebGUI::Friends->new($session,$user);
return 0 if ($me->isFriend($user->userId)); #Already a friend
my $friend = WebGUI::Friends->new($session,$user);
return 0 if ($me->isInvited($user->userId) || $friend->isInvited($self->userId)); #Invitation sent by one or the other
return $self->profileField('ableToBeFriend'); #Return profile setting
@ -594,7 +594,13 @@ sub hasFriends {
}
#-------------------------------------------------------------------
# This method is depricated and is provided only for reverse compatibility. See WebGUI::Auth instead.
=head2 identifier
This method is depricated and is provided only for reverse compatibility. See WebGUI::Auth instead.
=cut
sub identifier {
my ($self, $value);
$self = shift;
@ -635,8 +641,7 @@ The group that you wish to verify against the user. Defaults to group with Id 3
=cut
sub isInGroup {
my (@data, $groupId);
my ($self, $gid, $secondRun) = @_;
my ($self, $gid) = @_;
$gid = 3 unless $gid;
my $uid = $self->userId;
### The following several checks are to increase performance. If this section were removed, everything would continue to work as normal.
@ -997,11 +1002,12 @@ sub session {
=head2 setProfileFieldPrivacySetting ( settings )
Sets the profile field privacy settings
Sets the profile field privacy settings. This updates the the db and
the internally cached settings. Valid settings are "all", "none" or "friends".
=head3 settings
hash ref containing the field and it's corresponding privacy setting
Hash ref containing fields and their corresponding privacy settings
=cut

View file

@ -385,7 +385,7 @@ sub sortHash {
#-------------------------------------------------------------------
=head2 sortHashDecending ( hash )
=head2 sortHashDescending ( hash )
Sorts a hash in decending order by its values. Returns a Tie::IxHash.
You must assign this to a similarly tied hash to preserve the order.
@ -396,7 +396,6 @@ A hash to be sorted.
=cut
sub sortHashDescending {
my %hash = @_;
tie my %newHash, 'Tie::IxHash';
@ -408,5 +407,3 @@ sub sortHashDescending {
1;

View file

@ -23,12 +23,12 @@ use WebGUI::Inbox;
=head1 NAME
Package WebGUI::Workflow::Activity::NotifyAboutUser
Package WebGUI::Workflow::Activity::NotifyAboutLowStock
=head1 DESCRIPTION
Takes a user object and sends out a message. Can use macros in message, to and subject
fields.
Notify group about users when Products fall below a configurable amount of stock
in inventory.
=head1 SYNOPSIS

View file

@ -0,0 +1,110 @@
package WebGUI::Workflow::Activity::RemoveOldCarts;
=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::Workflow::Activity';
use WebGUI::International;
use WebGUI::Asset::Sku::Product;
use WebGUI::Inbox;
=head1 NAME
Package WebGUI::Workflow::Activity::RemoveOldCarts
=head1 DESCRIPTION
Remove carts that are older than a configurable threshold.
=head1 SYNOPSIS
See WebGUI::Workflow::Activity for details on how to use any activity.
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition ( session, definition )
See WebGUI::Workflow::Activity::defintion() for details.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, 'Workflow_Activity_RemoveOldCarts');
push(@{$definition}, {
name=>$i18n->get('activityName'),
properties=> {
cartTimeout => {
fieldType=>'interval',
label=>$i18n->get('cart timeout'),
defaultValue=>48*3600,
hoverHelp=>$i18n->get('cart timeout help'),
},
}
});
return $class->SUPER::definition($session,$definition);
}
#-------------------------------------------------------------------
=head2 execute ( [ object ] )
See WebGUI::Workflow::Activity::execute() for details.
=cut
sub execute {
my ($self) = @_;
my $session = $self->session;
my $now = time();
my $finishTime = $now + $self->getTTL;
my $expired = 0;
my $cartIds = [];
my $limit = $now - $self->get('cartTimeout');
$session->log->warn("limit: $limit");
my $expiredCarts = $session->db->read('select cartId from cart where creationDate < '.$limit);
$expiredCarts->execute();
CART: while( my ($cartId) = $expiredCarts->array() ) {
my $cart = eval {
WebGUI::Shop::Cart->new($session, $cartId);
};
next CART if WebGUI::Error->caught;
$session->log->warn("cartId: $cartId");
$cart->delete; ##Delete will empty, then delete.
##Time check and set flag
if (time() > $finishTime) {
$expired = 1;
last CART;
}
}
##If timer expired, then store message and limit and release
if ($expired) {
return $self->WAITING(1);
}
return $self->COMPLETE;
}
1;

View file

@ -0,0 +1,114 @@
package WebGUI::i18n::English::AssetAspect_RssFeed;
use strict;
our $I18N = {
'itemsPerFeed' => {
message => q|Items Per Feed|,
lastUpdated => 1236820473,
context => q|The name of the itemsPerFeed field.|
},
'itemsPerFeed hoverHelp' => {
message => q|The number of items to include in the feed.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the itemsPerFeed field.|
},
'feedCopyright' => {
message => q|Feed Copyright|,
lastUpdated => 1236820473,
context => q|The name of the feedCopyright field.|
},
'feedCopyright hoverHelp' => {
message => q|An optional copyright notice for the feed.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the feedCopyright field.|
},
'feedTitle' => {
message => q|Feed Title|,
lastUpdated => 1236820473,
context => q|The name of the feedTitle field.|
},
'feedTitle hoverHelp' => {
message => q|An optional title for the feed. If not specified the asset's title will be used instead.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the feedTitle field.|
},
'feedDescription' => {
message => q|Feed Description|,
lastUpdated => 1236820473,
context => q|The name of the feedDescription field.|
},
'feedDescription hoverHelp' => {
message => q|An optional description for the feed. If not specified the asset's synopsis will be used instead.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the feedDescription field.|
},
'feedImage' => {
message => q|Feed Image|,
lastUpdated => 1236820473,
context => q|The name of the feedImage field.|
},
'feedImage hoverHelp' => {
message => q|An optional image that can be uploaded for the feed.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the feedImage field.|
},
'feedImageLink' => {
message => q|Feed Image Link|,
lastUpdated => 1236820473,
context => q|The name of the feedImageLink field.|
},
'feedImageLink hoverHelp' => {
message => q|An optional URL that will link the image to a specific location. If not specified the asset's URL will be used instead.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the feedImageLink field.|
},
'feedImageDescription' => {
message => q|Feed Image Description|,
lastUpdated => 1236820473,
context => q|The name of the feedImageDescription field.|
},
'feedImageDescription hoverHelp' => {
message => q|An optional description for the image. If not specified the asset's title will be used instead.|,
lastUpdated => 1236820473,
context => q|The hoverhelp of the feedImageDescription field.|
},
'RSS tab' => {
message => q|RSS|,
lastUpdated => 1236820473,
context => q|The title of the RSS tab on the asset's edit form.|
},
'feedHeaderLinks' => {
message => q|HTML Header Feed Links|,
},
'feedHeaderLinks hoverHelp' => {
message => q|Select which feed types to include in the HTML headers, which many browsers will offer as options to users.|,
},
'rssLinkOption' => {
message => q|RSS 2.0|,
},
'atomLinkOption' => {
message => 'Atom',
},
'rdfLinkOption' => {
message => 'RDF/RSS 1.0',
},
};
1;
#vim:ft=perl

View file

@ -0,0 +1,266 @@
package WebGUI::i18n::English::Asset_AdSku;
use strict;
our $I18N = {
'assetName' => {
message => q|Ad Sales|,
lastUpdated => 0,
context => q|The name of the Ad Sales asset|,
},
'property purchase template' => {
message => q|Purchase Template|,
lastUpdated => 0,
context => q|The name of the template to use for purchasing ad space.|
},
'property purchase template help' => {
message => q|Select a template to use for purchasing ad space.|,
lastUpdated => 0,
context => q|Select a template to use for purchasing ad space.|
},
'property manage template' => {
message => q|Manage Template|,
lastUpdated => 0,
context => q|The name of the template to use for managing ad space.|
},
'property manage template help' => {
message => q|Select a template to use for managing ad space.|,
lastUpdated => 0,
context => q|Select a template to use for managing ad space.|
},
'property ad space' => {
message => q|Ad Space|,
lastUpdated => 0,
context => q|The ad space being sold here.|
},
'property ad Space help' => {
message => q|Select the ad space being sold by this SKU.|,
lastUpdated => 0,
context => q|Select the ad space being sold by this SKU.|
},
'property priority' => {
message => q|Priority|,
lastUpdated => 0,
context => q|The priority of the ads sold by this SKU.|
},
'property priority help' => {
message => q|Indicate the priority of ads sold by this SKU. You can use multiple SKU's to sell the same ad space at different rates by setting different priorities for each SKU.|,
lastUpdated => 0,
context => q|Help text for the priority field on the AdSku Edit page.|
},
'property price per click' => {
message => q|Price Per Click|,
lastUpdated => 0,
context => q|The price charged per click.|
},
'property price per click help' => {
message => q|Indicate how much to charge for each click purchased.|,
lastUpdated => 1165511641,
context => q|Help for the price per click field.|
},
'property price per impression' => {
message => q|Price Per Impression|,
lastUpdated => 0,
context => q|The price charged for each impression of this ad.|
},
'property price per impression help' => {
message => q|Indicate how much to charge for each impression purchased.|,
lastUpdated => 0,
context => q|Help text for the price per impression field.|
},
'property click discounts' => {
message => q|Click Discounts|,
lastUpdated => 0,
context => q|The discounts offered based on number of clicks.|
},
'property click discounts help' => {
message => q|Enter discounts one per line at the start of the line. Extra text is ignored so you can add comments to the discounts. Each discount consists of two numbers seperated by '@' with no spaces. The first number is the percent discount(no decimal point) the second number is the number of items purchased. So 5@1000 indicates a 5% discount for 1000 or more clicks purchased.|,
lastUpdated => 0,
context => q|Help text for the click discounts field.|
},
'property impression discounts' => {
message => q|Impression Discounts|,
lastUpdated => 0,
context => q|The discounts offered based on number of impressions purchased.|
},
'property impression discounts help' => {
message => q|Enter discounts one per line at the start of the line. Extra text is ignored so you can add comments to the discounts. Each discount consists of two numbers seperated by '@' with no spaces. The first number is the percent discount(no decimal point) the second number is the number of items purchased. So 5@1000 indicates a 5% discount for 1000 or more impressions purchased.|,
lastUpdated => 0,
context => q|Help text for the impresison discounts field.|
},
'property adsku karma' => {
message => q|karma|,
lastUpdated => 0,
context => q|The karm field name.|
},
'property adsku karma description' => {
message => q|how much karma does this offer|,
lastUpdated => 0,
context => q|Description for the karma field.|
},
'form purchase per click' => {
message => q|@ %f per click|,
lastUpdated => 0,
context => q|%f is the price charged for each click on the ad.|
},
'form purchase per impression' => {
message => q|@ %f per impression|,
lastUpdated => 0,
context => q|%f is the price charged for each impression of the ad.|
},
'form manage title' => {
message => q|Manage My Ads|,
lastUpdated => 0,
context => q|Text for the title of the form where the user can manage previously purchased advertisements.|
},
'form manage link' => {
message => q|Manage My Ads|,
lastUpdated => 0,
context => q|Text for a link to the form where the user can manage previously purchased advertisements.|
},
'form purchase link' => {
message => q|Purchase Ads|,
lastUpdated => 0,
context => q|Text for a link to the form where the user can purchase advertisements,|
},
'form manage table header title' => {
message => q|Title|,
lastUpdated => 0,
context => q|Header for the adspace manage form: the title field.|
},
'form manage table header clicks' => {
message => q|Clicks|,
lastUpdated => 0,
context => q|Header for the adspace manage form: the clicks field.|
},
'form manage table header impressions' => {
message => q|Impressions|,
lastUpdated => 0,
context => q|Header for the adspace manage form: the impressions field.|
},
'form manage table header renew' => {
message => q|Renew|,
lastUpdated => 0,
context => q|Header for the adspace manage form: the renew field.|
},
'form manage table value deleted' => {
message => q|Deleted|,
lastUpdated => 0,
context => q|Contents for the renew field on the manage ads table: indicates a deleted item.|
},
'form manage table value renew' => {
message => q|Renew|,
lastUpdated => 0,
context => q|Contents for the renew field on the manage ads table: indicates a renewable item.|
},
'form purchase button' => {
message => q|Add To Cart|,
lastUpdated => 0,
context => q|Add the described item to the shopping cart.|
},
'form purchase ad title' => {
message => q|Ad Title|,
lastUpdated => 0,
context => q|The title chosen by the buyer for the advertisement.|
},
'form purchase ad link' => {
message => q|Ad Link|,
lastUpdated => 0,
context => q|The link the advertisement leads to.|
},
'form purchase ad image' => {
message => q|Image|,
lastUpdated => 0,
context => q|The image to be displayed in the ad.|
},
'form purchase number of clicks' => {
message => q|Number of Clicks|,
lastUpdated => 0,
context => q|The number of clicks the buyer wishes to purchase.|
},
'form purchase number of impressions' => {
message => q|Number of Impressions|,
lastUpdated => 0,
context => q|The number of impressions the user wishes to purchase.|
},
'form added to cart thanks' => {
message => q|Thank you very much for your purchase.|,
lastUpdated => 0,
context => q|Thank the customer after adding the item to the cart.|
},
'form error no image' => {
message => q|Please assign an image for this ad.|,
lastUpdated => 0,
context => q|remind the user to upload an image for the ad.|
},
'form error no title' => {
message => q|Please enter the title for this ad.|,
lastUpdated => 0,
context => q|Remind the user to enter a title for the ad.|
},
'form error no link' => {
message => q|Please enter a valid URL for this ad.|,
lastUpdated => 0,
context => q|Remind the user to enter a valid URL for the ad.|
},
'form error min clicks' => {
message => q|You must purchase at least %d clicks for this adSpace.|,
lastUpdated => 0,
context => q|Remind the user to that they must purchase a minimum number of clicks, use '%d' to indicate the minimum number of clicks.|
},
'form error min impressions' => {
message => q|You must purchase at least %d impressions for this adSpace.|,
lastUpdated => 0,
context => q|Remind the user to that they must purchase a minimum number of impressions, use '%d' to indicate the minimum number of impressions.|
},
# 'TODO' => {
# message => q|TODO|,
# lastUpdated => 0,
# context => q|TODO|
# },
};
1;

View file

@ -0,0 +1,80 @@
package WebGUI::i18n::English::Asset_Carousel;
use strict;
our $I18N = {
'assetName' => {
message => q|Carousel|,
lastUpdated => 0,
context => q|The name of this asset, used in the admin bar.|
},
'carousel template label' => {
message => q|Carousel template|,
lastUpdated => 0,
context => q|Label of the carousel template field on the edit screen.|
},
'carousel template description' => {
message => q|Select a template for this carousel.|,
lastUpdated => 0,
context => q|Description of the carousel template field, used as hover help.|
},
'items label' => {
message => q|Items|,
lastUpdated => 0,
context => q|Label of the items field on the edit screen.|
},
'items description' => {
message => q|Enter this carousel's items.|,
lastUpdated => 0,
context => q|Description of the items field, used as hover help.|
},
'id label' => {
message => q|ID|,
lastUpdated => 0,
context => q|Label of the item ID field on the edit screen.|
},
'id description' => {
message => q|Enter a unique ID for this carousel item.|,
lastUpdated => 0,
context => q|Description of the item ID field, used as hover help.|
},
'carousel template help title' => {
message => q|Carousel Template Variables|,
lastUpdated => 0,
context => q|Title of a template help page.|
},
'item_loop' => {
message => q|A loop containing this carousel's items.|,
lastUpdated => 0,
context => q|Description of the item_loop tmpl_loop for the template help.|
},
'itemId' => {
message => q|This carousel item's id.|,
lastUpdated => 0,
context => q|Description of the itemId tmpl_var for the template help.|
},
'text' => {
message => q|This carousel item's text.|,
lastUpdated => 0,
context => q|Description of the text tmpl_var for the template help.|
},
'sequenceNumber' => {
message => q|This carousel item's sequenceNumber.|,
lastUpdated => 0,
context => q|Description of the sequenceNumber tmpl_var for the template help.|
}
};
1;
#vim:ft=perl

View file

@ -127,6 +127,7 @@ our $I18N = {
lastUpdated => 0,
context => 'Label for button to submit search form',
},
"templateIdAddArchive label" => {
message => "Template to Add Multiple",
lastUpdated => 0,
@ -741,6 +742,25 @@ our $I18N = {
lastUpdated => 0,
context => q{Option label for 300 pixels-per-inch images, good for printing images},
},
'template listAlbumsRss' => {
message => 'Subscribe',
lastUpdated => 1237403207,
context => 'Label for link to RSS feed.',
},
'template search basic title' => {
message => 'Search Gallery',
lastUpdated => 1237403442,
context => 'Title for basic search form in page.',
},
'template search basic term' => {
message => 'Search Term',
lastUpdated => 1237403498,
context => 'Title for basic search term field in page.',
},
};
1;

View file

@ -574,6 +574,18 @@ our $I18N = {
context => q{template label in the Edit Album template. To pick a thumbnail to use to represent the Album.},
},
'template album thumbnail alt' => {
message => 'Preview thumbnail for',
lastUpdated => 1237404629,
context => 'Alt text for album thumbnail.',
},
'template url_addDescription' => {
message => 'Add a Description',
lastUpdated => 1237405184,
context => 'Label for URL to add an album Description.',
},
};
1;

View file

@ -77,6 +77,54 @@ shown here.|,
lastUpdated => 1106765841
},
'annotate' => {
message => q|Annotate|,
context => q|label to annotate the image|,
lastUpdated => 1106765841
},
'annotate image' => {
message => q|Annotate Image|,
context => q|label to annotate the image|,
lastUpdated => 1106765841
},
'annotate image description' => {
message => q|Text Around the Image|,
context => q|label to annotate the image|,
lastUpdated => 1106765841
},
'degree' => {
message => q|Degrees to Rotate|,
context => q|label to rotate the image|,
lastUpdated => 1106765841
},
'rotate image label' => {
message => q|Please click to rotate image|,
context => q|label to rotate the image|,
lastUpdated => 1106765841
},
'rotate image' => {
message => q|Rotate Image|,
context => q|label to rotate the image|,
lastUpdated => 1106765841
},
'rotate image label' => {
message => q|Please click to rotate image|,
context => q|label to rotate the image|,
lastUpdated => 1106765841
},
'crop image' => {
message => q|Crop Image|,
context => q|label to crop the image|,
lastUpdated => 1106765841
},
'new width' => {
message => q|New Width|,
context => q|label to resize the image|,
@ -101,6 +149,42 @@ shown here.|,
lastUpdated => 1130538987
},
'undo image' => {
message => q|Undo Image|,
context => q|undo editing operations|,
lastUpdated => 1106765841
},
'delete' => {
message => q|Delete|,
context => q|label to delete annotation|,
lastUpdated => 1106765841
},
'height' => {
message => q|Height|,
context => q|label to resize the image|,
lastUpdated => 1106765841
},
'width' => {
message => q|Width|,
context => q|label to resize the image|,
lastUpdated => 1106765841
},
'top' => {
message => q|Top|,
context => q|label to resize the image|,
lastUpdated => 1106765841
},
'left' => {
message => q|Left|,
context => q|label to resize the image|,
lastUpdated => 1106765841
},
'image template title' => {
message => q|Image Template Variables|,
lastUpdated => 1184820779,

View file

@ -427,6 +427,17 @@ listing,|,
lastUpdated => 1235681965,
},
'statistics cache timeout label' => {
message => q|Statistics Cache Timeout|,
lastUpdated => 0,
},
'statistics cache timeout description' => {
message => q|Since all users will see the matrix statistics the same way, we can cache them for some time
to increase performance. How long should we cache them?|,
lastUpdated => 0,
},
'rating timeout description' => {
message => q|Set a timeout so that users are prevented from rating a given listing too often.|,
lastUpdated => 1135271460,

View file

@ -136,10 +136,10 @@ all of the classes of pages that should be included:<br />
<dd>The current page.</dd>
<dt>Siblings</dt>
<dd>Pages at the same level as the current URL.</dd>
<dt>Descendents</dt>
<dt>Descendants</dt>
<dd>Pages lower than the current page in the tree.</dd>
<dt>Pedigree</dt>
<dd>When using a different start page, this option selects the Ancestors, Siblings and Descendents of that page.</dd>
<dd>When using a different start page, this option selects the Ancestors, Siblings and Descendants of that page.</dd>
</dl>|,
lastUpdated => 1146456217,
},
@ -372,7 +372,7 @@ the Navigation Template to determine who can see them in the menu.</p>
lastUpdated => 1163720148,
},
'page.isDescendent' => {
'page.isDescendant' => {
message => q|A conditional indicating whether this page is a descendant of the current page.|,
lastUpdated => 1163720154,
},
@ -393,7 +393,7 @@ the Navigation Template to determine who can see them in the menu.</p>
},
'page.inBranch' => {
message => q|A conditional that is the logical OR of <strong>isAncestor</strong>, <strong>isSibling</strong>, <strong>isBasepage</strong> and <strong>isDescendent</strong>.|,
message => q|A conditional that is the logical OR of <strong>isAncestor</strong>, <strong>isSibling</strong>, <strong>isBasepage</strong> and <strong>isDescendant</strong>.|,
lastUpdated => 1157647394,
},

View file

@ -1,14 +0,0 @@
package WebGUI::i18n::English::Asset_RSSCapable;
use strict;
our $I18N =
{
'rssEnabled label' => { message => 'Enable RSS', lastUpdate => 1162487361 },
'rssEnabled hoverHelp' => { message => q|Whether or not to enable the RSS feed for this asset. If enabled, an RSS From Parent asset will be created and managed as an extra child for this purpose. If not enabled, no such child will be created and the existing one will be deleted.|, lastUpdate => 1162487361 },
'rssTemplateId label' => { message => 'RSS Template', lastUpdate => 1162487361 },
'rssTemplateId hoverHelp' => { message => q|The template to use for the RSS feed of this asset.|, lastUpdate => 1162487361 },
'assetName' => { message => 'RSS Capable', lastUpdate => 1162487361 },
};
1;

View file

@ -1,95 +0,0 @@
package WebGUI::i18n::English::Asset_RSSFromParent;
use strict;
our $I18N =
{
'assetName' => { message => 'RSS From Parent', lastUpdated => 1162257377 },
'rss from parent title' => {
message => 'RSS From Parent',
lastUpdated => 1162257377
},
'rss from parent body' => {
message => q|<p>The sole purpose of this Asset is to provide a base template for generating RSS Feeds. Below are listed the basic template variables available to any valid RSS generating Asset. Assets may provide additional or different variables.</p>|,
lastUpdated => 1162257377
},
'title.parent' => {
message => 'The title of the parent of this Asset',
lastUpdated => 1162257377
},
'title.item' => {
message => 'The title of this Asset',
lastUpdated => 1162257377
},
'link' => {
message => 'link',
lastUpdated => 1162257377
},
'link.parent' => {
message => 'The url of the parent of this Asset',
lastUpdated => 1162257377
},
'link.item' => {
message => 'The url of this Asset',
lastUpdated => 1162257377
},
'description' => {
message => 'description',
lastUpdated => 1162257377
},
'description.parent' => {
message => 'The description of the parent of this Asset',
lastUpdated => 1162257377
},
'description.item' => {
message => 'The description of this Asset',
lastUpdated => 1162257377
},
'generator' => {
message => 'A string that identifies that this RSS was generated by WebGUI and also by what version of WebGUI.',
lastUpdated => 1162257377
},
'lastBuildDate' => {
message => q|The date the parent's content was last modified in the proper format for RSS (RFC 822).|,
lastUpdated => 1162257377
},
'webMaster' => {
message => q|The company email address from the WebGUI Settings.|,
lastUpdated => 1162257377
},
'docs' => {
message => q|The URL http://blogs.law.harvard.edu/tech/rss, which links to the RSS 2.0 specification.|,
lastUpdated => 1162257377
},
'item_loop' => {
message => q|A loop containing information about all Assets below the parent.|,
lastUpdated => 1162957711
},
'guid' => {
message => q|An alias for link. In RSS, guid is the unique identifier for this item.|,
lastUpdated => 1162957711
},
'pubDate' => {
message => q|The date this item was last modified in the proper format for RSS (RFC 822).|,
lastUpdated => 1162958127
},
};
1;

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