Merge commit 'v7.10.15' into 8

Conflicts:
	docs/gotcha.txt
	docs/previousVersion.sql
	docs/templates.txt
	lib/WebGUI.pm
	lib/WebGUI/Asset.pm
	lib/WebGUI/Asset/Event.pm
	lib/WebGUI/Asset/File.pm
	lib/WebGUI/Asset/MapPoint.pm
	lib/WebGUI/Asset/RichEdit.pm
	lib/WebGUI/Asset/Sku/Product.pm
	lib/WebGUI/Asset/Snippet.pm
	lib/WebGUI/Asset/Story.pm
	lib/WebGUI/Asset/Template.pm
	lib/WebGUI/Asset/Template/TemplateToolkit.pm
	lib/WebGUI/Asset/Wobject/Calendar.pm
	lib/WebGUI/Asset/Wobject/Carousel.pm
	lib/WebGUI/Asset/Wobject/Collaboration.pm
	lib/WebGUI/Asset/Wobject/Dashboard.pm
	lib/WebGUI/Asset/Wobject/DataForm.pm
	lib/WebGUI/Asset/Wobject/Folder.pm
	lib/WebGUI/Asset/Wobject/Map.pm
	lib/WebGUI/Asset/Wobject/Search.pm
	lib/WebGUI/Asset/Wobject/Shelf.pm
	lib/WebGUI/Asset/Wobject/StockData.pm
	lib/WebGUI/Asset/Wobject/StoryTopic.pm
	lib/WebGUI/Asset/Wobject/SyndicatedContent.pm
	lib/WebGUI/Asset/Wobject/Thingy.pm
	lib/WebGUI/Asset/Wobject/WeatherData.pm
	lib/WebGUI/AssetClipboard.pm
	lib/WebGUI/AssetCollateral/DataForm/Entry.pm
	lib/WebGUI/AssetExportHtml.pm
	lib/WebGUI/AssetLineage.pm
	lib/WebGUI/AssetMetaData.pm
	lib/WebGUI/AssetTrash.pm
	lib/WebGUI/AssetVersioning.pm
	lib/WebGUI/Auth.pm
	lib/WebGUI/Cache/CHI.pm
	lib/WebGUI/Content/AssetManager.pm
	lib/WebGUI/Fork/ProgressBar.pm
	lib/WebGUI/Form/JsonTable.pm
	lib/WebGUI/Form/TimeField.pm
	lib/WebGUI/Form/Zipcode.pm
	lib/WebGUI/Group.pm
	lib/WebGUI/International.pm
	lib/WebGUI/Macro/AssetProxy.pm
	lib/WebGUI/Macro/FileUrl.pm
	lib/WebGUI/Operation/SSO.pm
	lib/WebGUI/Operation/User.pm
	lib/WebGUI/Role/Asset/Subscribable.pm
	lib/WebGUI/Shop/Cart.pm
	lib/WebGUI/Shop/Transaction.pm
	lib/WebGUI/Shop/TransactionItem.pm
	lib/WebGUI/Test.pm
	lib/WebGUI/URL/Content.pm
	lib/WebGUI/URL/Uploads.pm
	lib/WebGUI/User.pm
	lib/WebGUI/Workflow/Activity/ExtendCalendarRecurrences.pm
	lib/WebGUI/Workflow/Activity/SendNewsletters.pm
	lib/WebGUI/i18n/English/Asset.pm
	lib/WebGUI/i18n/English/WebGUI.pm
	sbin/installClass.pl
	sbin/rebuildLineage.pl
	sbin/search.pl
	sbin/testEnvironment.pl
	t/Asset/Asset.t
	t/Asset/AssetClipboard.t
	t/Asset/AssetLineage.t
	t/Asset/AssetMetaData.t
	t/Asset/Event.t
	t/Asset/File.t
	t/Asset/File/Image.t
	t/Asset/Post/notification.t
	t/Asset/Sku.t
	t/Asset/Story.t
	t/Asset/Template.t
	t/Asset/Wobject/Collaboration/templateVariables.t
	t/Asset/Wobject/Collaboration/unarchiveAll.t
	t/Asset/Wobject/Shelf.t
	t/Auth.t
	t/Macro/EditableToggle.t
	t/Macro/FilePump.t
	t/Shop/Cart.t
	t/Shop/Transaction.t
	t/Storage.t
	t/User.t
	t/Workflow.t
This commit is contained in:
Doug Bell 2011-05-13 18:15:11 -05:00
commit 277faae8a1
783 changed files with 32041 additions and 25495 deletions

View file

@ -188,7 +188,7 @@ sub www_editFriends {
my @manageableUsers = ();
if ($groupName) { # Only adding users from a single group
my $group = WebGUI::Group->find($session, $groupName);
push @manageableUsers, @{ $group->getUsersNotIn($user->{_user}->{'friendsGroup'}, 'withoutExpired') };
push @manageableUsers, @{ $group->getUsersNotIn($user->friends->getId, 'withoutExpired') };
}
else { # Defaults to groups selected in settings
my $groupIds = $session->setting->get('groupsToManageFriends');
@ -196,7 +196,7 @@ sub www_editFriends {
foreach my $groupId (@groupIds) {
my $group = WebGUI::Group->new($session, $groupId);
next GROUP unless $group->getId || $group->getId eq 'new';
push @manageableUsers, @{ $group->getUsersNotIn($user->{_user}->{'friendsGroup'}, 'withoutExpired') };
push @manageableUsers, @{ $group->getUsersNotIn($user->friends->getId, 'withoutExpired') };
}
@manageableUsers = uniq @manageableUsers;
}

View file

@ -177,7 +177,7 @@ property extraHeadTags => (
around extraHeadTags => sub {
my $orig = shift;
my $self = shift;
if (@_ > 1) {
if (@_ > 0) {
my $unpacked = $_[0];
my $packed = $unpacked; ##Undo magic aliasing since a reference is passed below
HTML::Packer::minify( \$packed, {
@ -1709,6 +1709,19 @@ sub getViewCacheKey {
#-------------------------------------------------------------------
=head2 getContentLastModifiedBy ( )
Returns the userId that modified the content last.
=cut
sub getContentLastModifiedBy {
my $self = shift;
return $self->get("revisedBy");
}
#-------------------------------------------------------------------
=head2 getWwwCacheKey ( )
Returns a cache object specific to this asset, and whether or not the request is in SSL mode.
@ -2387,7 +2400,7 @@ sub publish {
$self->session->db->write("update asset set state='published', stateChangedBy=".$self->session->db->quote($self->session->user->userId).", stateChanged=".time()." where assetId in (".$idList.")");
foreach my $id (@{$assetIds}) {
my $asset = WebGUI::Asset->newById($self->session, $id);
my $asset = WebGUI::Asset->newPending($self->session, $id);
if (defined $asset) {
$asset->purgeCache;
}
@ -2425,6 +2438,21 @@ sub purgeCache {
}
#-------------------------------------------------------------------
=head2 refused ( )
Returns an error message to the user, wrapped in the user's style. This is most useful for
handling UI errors. Privilege errors should be still be sent to $session->privilege.
=cut
sub refused {
my ($self) = @_;
return $self->{_session};
}
#-------------------------------------------------------------------
=head2 session ( )
@ -2683,10 +2711,10 @@ sub www_addSave {
my ( $form ) = $session->quick(qw{ form });
return $session->privilege->insufficient() unless $self->canEdit;
if ($self->session->config("maximumAssets")) {
if ($self->session->config->get("maximumAssets")) {
my ($count) = $self->session->db->quickArray("select count(*) from asset");
my $i18n = WebGUI::International->new($self->session, "Asset");
return $self->session->style->userStyle($i18n->get("over max assets")) if ($self->session->config("maximumAssets") <= $count);
return $self->session->style->userStyle($i18n->get("over max assets")) if ($self->session->config->get("maximumAssets") <= $count);
}
# Determine what version tag we should use

View file

@ -27,6 +27,7 @@ use WebGUI::Storage;
use Test::Deep::NoTest qw(eq_deeply);
use DateTime::Event::ICal;
use DateTime::Set;
use Data::ICal::Entry::Event;
use Moose;
use WebGUI::Definition::Asset;
@ -212,6 +213,54 @@ override addRevision => sub {
return $newRev;
};
####################################################################
=head2 add_to_calendar ($iCal)
Build a Data::ICal::Entry::Event object that contains the information for this
event and add it to the Data::ICal calendar
=head3 $iCal
A Data::ICal object, representing the top-level calendar instance.
=cut
sub add_to_calendar {
my $self = shift;
my $session = $self->session;
my $calendar = shift;
my $event = Data::ICal::Entry::Event->new();
$event->add_properties(
'last-modified' => WebGUI::DateTime->new($session, $event->get("revisionDate"))->toIcal,
created => WebGUI::DateTime->new($session, $event->get("creationDate"))->toIcal,
sequence => $self->get('iCalSequenceNumber'),
summary => $self->get('title'),
description => $self->get('description'),
location => $self->get('location'),
uid => $self->get('feedUid')
? $self->get('feedUid')
: $self->get('assetId') . '@'. $session->config->get("sitename")->[0],
);
##WebGUI Specific fields
foreach my $prop (qw/groupIdView groupIdEdit url menuTitle timeZone/) {
$event->add_property( 'x-webgui-'.lc($prop) => $self->get($prop));
}
my $eventStart = $self->getIcalStart;
my $start_parameters = {};
if (! $eventStart =~ /T/) {
$start_parameters->{VALUE} = 'DATE';
}
$event->add_property(dtstart => [ $eventStart, $start_parameters ]);
my $eventEnd = $self->getIcalEnd;
my $end_parameters = {};
if (! $eventEnd =~ /T/) {
$end_parameters->{VALUE} = 'DATE';
}
$event->add_property(dtend => [ $eventEnd, $end_parameters ]);
$calendar->add_entry($event);
}
{
my %dayNamesToICal = (
@ -411,6 +460,14 @@ sub duplicate {
my $newAsset = $self->SUPER::duplicate(@_);
my $newStorage = $self->getStorageLocation->copy;
$newAsset->update({storageId=>$newStorage->getId});
my $links = $self->getRelatedLinks();
my $id = $self->session->id;
foreach my $link (@{ $links }) {
$link->{new_event} = 1;
$link->{eventlinkId} = $id->generate;
$link->{linkurl} = $link->{linkURL};
}
$newAsset->setRelatedLinks($links);
return $newAsset;
}
@ -434,13 +491,12 @@ sub generateRecurrence {
WHERE recurId = ? AND startDate = ?
};
my $db = $self->session->db;
unless ($db->quickScalar($sql, [$self->recurId, $sdb])) {
my $child = $self->get;
$child->{startDate} = $sdb;
$child->{endDate} = $edb;
$self->getParent->addChild(
$child, undef, undef, { skipAutoCommitWorkflows => 1 }
);
unless ($db->quickScalar($sql, [$self->get('recurId'), $sdb])) {
my $child = $self->duplicate({skipAutoCommitWorkflows => 1});
$child->update({
startDate => $sdb,
endDate => $edb,
});
}
}
@ -1374,9 +1430,18 @@ override processEditForm => sub {
### Verify the form was filled out correctly...
my @errors;
# If the start date is after the end date
my $i18n = WebGUI::International->new($session, 'Asset_Event');
if ($self->startDate gt $self->endDate) {
# Verify we got valid dates
if ( !eval{ $self->getDateTimeStart; 1 } ) {
push @errors, $i18n->get('invalid start date');
}
if ( !eval{ $self->getDateTimeEnd; 1 } ) {
push @errors, $i18n->get('invalid end date');
}
# If the start date is after the end date
if ($self->get("startDate") gt $self->get("endDate")) {
push @errors, $i18n->get("The event end date must be after the event start date.");
}
@ -1605,13 +1670,17 @@ Extent the method from the super class to delete all storage locations.
override purge => sub {
my $self = shift;
my $sth = $self->session->db->read("select storageId from Event where assetId=?",[$self->getId]);
while (my ($storageId) = $sth->array) {
my $storage = WebGUI::Storage->get($self->session,$storageId);
my $id = $self->getId;
my $session = $self->session;
my @storageIds = $session->db->buildArray("select storageId from Event where assetId=?",[$id]);
my $success = $self->SUPER::purge;
return 0 unless $success;
foreach my $storageId (@storageIds) {
my $storage = WebGUI::Storage->get($session, $storageId);
$storage->delete if defined $storage;
}
$sth->finish;
return super();
$session->db->write('delete from Event_relatedlink where assetId=?',[$id]);
return 1;
};
#-------------------------------------------------------------------
@ -2235,20 +2304,24 @@ sub www_edit {
# End
$var->{"formRecurEnd"}
= q|
<div><input type="radio" name="recurEndType" id="recurEndType_none" value="none" |.(!$recur->{endDate} && !$recur->{endAfter} ? 'checked="checked"' : '').q|/>
<label for="recurEndType_none">|. $i18n->get('No end'). q|</label><br />
<div><input type="radio" name="recurEndType" id="recurEndType_none" value="none" |.(!$recur->{endDate} && !$recur->{endAfter} ? 'checked="checked"' : '').q| onclick="toggleRecurEnd()"/>
<label for="recurEndType_none">|. $i18n->get('No end'). q|</label>
<input type="radio" name="recurEndType" id="recurEndType_date" value="date" |.($recur->{endDate} ? 'checked="checked"' : '' ).q| />
<label for="recurEndType_date">|. $i18n->get('By date'). q| </label>|
<input type="radio" name="recurEndType" id="recurEndType_date" value="date" |.($recur->{endDate} ? 'checked="checked"' : '' ).q| onclick="toggleRecurEnd()"/>
<label for="recurEndType_date">|. $i18n->get('By date'). q| </label>
<div id="recurEndPattern_date"> |
. WebGUI::Form::date($session,{ name => "recurEndDate", value => $recur->{endDate}, defaultValue => $recur->{endDate} })
. q|
<br />
</div>
<input type="radio" name="recurEndType" id="recurEndType_after" value="after" |.($recur->{endAfter} ? 'checked="checked"' : '' ).q| />
<input type="radio" name="recurEndType" id="recurEndType_after" value="after" |.($recur->{endAfter} ? 'checked="checked"' : '' ).q| onclick="toggleRecurEnd()"/>
<label for="recurEndType_after">|. $i18n->get('After'). q| </label>
<div id="recurEndPattern_after">
<input type="text" size="3" name="recurEndAfter" value="|.$recur->{endAfter}.q|" />
|. $i18n->get('occurences'). q|.
</div>
</div>
|;
# Include
@ -2310,7 +2383,19 @@ sub www_edit {
document.getElementById("recurPattern_yearly").style.display = "block";
}
}
YAHOO.util.Event.onAvailable("recurPattern",function(e) { toggleRecur(); });
function toggleRecurEnd() {
document.getElementById("recurEndPattern_date").style.display = "none";
document.getElementById("recurEndPattern_after").style.display = "none";
if (document.getElementById("recurEndType_date").checked) {
document.getElementById("recurEndPattern_date").style.display = "block";
}
else if (document.getElementById("recurEndType_after").checked) {
document.getElementById("recurEndPattern_after").style.display = "block";
}
}
YAHOO.util.Event.onAvailable("recurPattern",function(e) { toggleRecur(); toggleRecurEnd(); });
</script>
ENDJS

View file

@ -133,6 +133,24 @@ sub setPrivileges {
);
}
#----------------------------------------------------------------------------
=head2 commit ( )
Override commit to remove all privileges for previous revisions' storage
locations
=cut
sub commit {
my ( $self, @args ) = @_;
for my $rev ( grep { $_->get("revisionDate") < $self->get("revisionDate") } @{$self->getRevisions} ) {
$rev->getStorageLocation->trash;
}
return $self->SUPER::commit( @args );
}
#-------------------------------------------------------------------
@ -401,7 +419,6 @@ override processEditForm => sub {
return undef;
};
#-------------------------------------------------------------------
=head2 purge
@ -450,6 +467,20 @@ override purgeRevision => sub {
#----------------------------------------------------------------------------
=head2 restore ( )
Override trash restore to restore storage location
=cut
sub restore {
my ( $self, @args ) = @_;
$self->setPrivileges;
return $self->SUPER::restore( @args );
}
#----------------------------------------------------------------------------
=head2 setFile ( [pathtofile] )
Tells the asset to do all the postprocessing on the file (setting privs, thubnails, or whatever).
@ -535,6 +566,23 @@ sub setStorageLocation {
#----------------------------------------------------------------------------
=head2 trash ( )
Override to put the attached file in the trash too
=cut
sub trash {
my ( $self, @args ) = @_;
my $return = $self->SUPER::trash( @args );
$self->getStorageLocation->trash;
return $return;
}
#----------------------------------------------------------------------------
=head2 updatePropertiesFromStorage ( )
Updates the asset properties from the file tracked by this asset. Should be

View file

@ -17,6 +17,8 @@ package WebGUI::Asset::MapPoint;
use strict;
use Moose;
use WebGUI::Definition::Asset;
use Geo::Coder::Googlev3;
extends 'WebGUI::Asset';
define assetName => ['assetName', 'Asset_MapPoint'];
define icon => 'mappoint.png';
@ -121,6 +123,12 @@ property userDefined5 => (
fieldType => "hidden",
noFormPost => 1,
);
property isGeocoded => (
fieldType => "yesNo",
tab => "properties",
label => ["isGeocoded label",'Asset_MapPoint'],
hoverHelp => ["isGeocoded description",'Asset_MapPoint'],
);
=head1 NAME
@ -184,6 +192,7 @@ AT LEAST the following keys:
title - The title of the point
content - HTML content to show details about the point
url - The URL of the point
userDefined1-5 - The userDefined fields
The following keys are optional
@ -199,7 +208,7 @@ sub getMapInfo {
# Get asset properties
$var->{ url } = $self->getUrl;
$var->{ assetId } = $self->getId;
my @keys = qw( latitude longitude title );
my @keys = qw( latitude longitude title userDefined1 userDefined2 userDefined3 userDefined4 userDefined5 isGeocoded );
for my $key ( @keys ) {
$var->{ $key } = $self->$key;
}
@ -246,6 +255,15 @@ sub getTemplateVarsEditForm {
my $session = $self->session;
my $var = $self->getTemplateVars;
my $parent = $self->getParent;
#If it's a new point, we have to get the parent from the url
unless ($parent) {
my $url = $session->url->page;
$parent = WebGUI::Asset->newByUrl($session,$url);
}
$var->{'can_edit_map'} = $parent->canEdit;
$var->{ form_header }
= WebGUI::Form::formHeader( $session )
. WebGUI::Form::hidden( $session, {
@ -293,8 +311,25 @@ sub getTemplateVarsEditForm {
name => "synopsis",
value => $self->synopsis,
resizable => 0,
} );
} );
#Only allow people who can edit the parent to change isHidden
if($var->{'can_edit_map'}) {
my $isHidden = (defined $self->get("isHidden")) ? $self->get("isHidden") : 1;
$var->{ "form_isHidden" }
= WebGUI::Form::yesNo( $session, {
name => "isHidden",
value => $isHidden,
} );
}
my $isGeocoded = ( $self->getId ) ? $self->get("isGeocoded") : 1;
$var->{"form_isGeocoded"}
= WebGUI::Form::checkbox( $session, {
name => "isGeocoded",
value => 1,
checked => $isGeocoded
} );
# Fix storageIdPhoto because scripts do not get executed in ajax requests
$var->{ "form_storageIdPhoto" }
= '<input type="file" name="storageIdPhoto" />';
@ -309,6 +344,35 @@ sub getTemplateVarsEditForm {
#-------------------------------------------------------------------
=head2 indexContent ( )
Indexing the content of attachments and user defined fields. See WebGUI::Asset::indexContent() for additonal details.
=cut
sub indexContent {
my $self = shift;
my $indexer = $self->SUPER::indexContent;
$indexer->addKeywords($self->get("website"));
$indexer->addKeywords($self->get("address1"));
$indexer->addKeywords($self->get("address2"));
$indexer->addKeywords($self->get("city"));
$indexer->addKeywords($self->get("region"));
$indexer->addKeywords($self->get("zipCode"));
$indexer->addKeywords($self->get("country"));
$indexer->addKeywords($self->get("phone"));
$indexer->addKeywords($self->get("fax"));
$indexer->addKeywords($self->get("email"));
$indexer->addKeywords($self->get("userDefined1"));
$indexer->addKeywords($self->get("userDefined2"));
$indexer->addKeywords($self->get("userDefined3"));
$indexer->addKeywords($self->get("userDefined4"));
$indexer->addKeywords($self->get("userDefined5"));
return $indexer;
}
#-------------------------------------------------------------------
=head2 processAjaxEditForm ( )
Process the Ajax Edit Form from the Map. If any errors occur, return
@ -336,6 +400,29 @@ sub processAjaxEditForm {
$prop->{ synopsis } = $form->get('synopsis');
$prop->{ url } = $session->url->urlize( $self->getParent->getUrl . '/' . $prop->{title} );
$prop->{ ownerUserId } = $form->get('ownerUserId') || $session->user->userId;
#Only users who can edit the map can set this property
if($self->getParent->canEdit) {
$prop->{ isHidden } = $form->get('isHidden');
}
$prop->{isGeocoded } = $form->get('isGeocoded') || 0;
if($prop->{isGeocoded} &&
(
( $form->get("address1") ne $self->get("address1") )
|| ( $form->get("address2") ne $self->get("address2") )
|| ( $form->get("city") ne $self->get("city") )
|| ( $form->get("region") ne $self->get("region") )
|| ( $form->get("zipCode") ne $self->get("zipCode") )
|| ( $form->get("country") ne $self->get("country") )
)
) {
my $geocoder = Geo::Coder::Googlev3->new;
my $address_str = $form->get("address1");
$address_str .= " ".$form->get("address2") if($form->get("address2"));
$address_str .= ", ".$form->get("city").", ".$form->get("region").", ".$form->get("zipCode").", ".$form->get("country");
my $location = $geocoder->geocode( location => $address_str );
$prop->{latitude } = $location->{geometry}->{location}->{lat};
$prop->{longitude} = $location->{geometry}->{location}->{lng};
}
$self->update( $prop );

View file

@ -235,12 +235,16 @@ Returns a boolean indicating whether the user can view the current post.
sub canView {
my $self = shift;
if (($self->status eq "approved" || $self->status eq "archived") && $self->getThread->getParent->canView) {
my $userId = shift || $self->session->user->userId;
$self->session->log->info( "$userId " . $self->status );
if (($self->status eq "approved" || $self->status eq "archived") && $self->getThread->getParent->canView( $userId )) {
$self->session->log->info( "CAN VIEW" );
return 1;
} elsif ($self->canEdit) {
} elsif ($self->canEdit( $userId )) {
$self->session->log->info( "CAN EDIT" );
return 1;
} else {
$self->getThread->getParent->canEdit;
return $self->getThread->getParent->canEdit( $userId );
}
}
@ -362,6 +366,26 @@ sub disqualifyAsLastPost {
#-------------------------------------------------------------------
=head2 duplicate ( )
Extend the base method to handle duplicate storage locations and groups.
=cut
sub duplicate {
my $self = shift;
my $session = $self->session;
my $copy = $self->SUPER::duplicate(@_);
if ($self->get('storageId')) {
my $storage = $self->getStorageLocation;
my $copied_storage = $storage->copy;
$copy->update({storageId => $copied_storage->getId});
}
return $copy;
}
#-------------------------------------------------------------------
=head2 DESTROY
Extend the base method to delete the locally cached thread object.

View file

@ -247,6 +247,27 @@ sub DEMOLISH {
#-------------------------------------------------------------------
=head2 duplicate
Extends the base method to handle creating a new subscription group.
=cut
sub duplicate {
my $self = shift;
my $session = $self->session;
my $copy = $self->SUPER::duplicate(@_);
if ($self->get('subscriptionGroupId')) {
my $group = WebGUI::Group->new($session, $self->get('subscriptionGroupId'));
my $copied_group = WebGUI::Group->new($session, 'new');
$copied_group->addUsers($group->getUsers('withoutExpired'));
$copy->update({subscriptionGroupId => $copied_group->getId});
}
return $copy;
}
#-------------------------------------------------------------------
=head2 getAdjacentThread ( )
Given a field and an order, returns the nearest thread when sorting by those.

View file

@ -326,7 +326,140 @@ override getEditForm => sub {
return $f;
};
#----------------------------------------------------------------------------
=head2 getAllButtons ( )
Get a list of all the buttons in this MCE
=cut
sub getAllButtons {
my ( $self ) = @_;
my @toolbarRows = map{[split "\n", $self->get("toolbarRow$_")]} (1..3);
my @toolbarButtons = map{ @{$_} } @toolbarRows;
return @toolbarButtons;
}
#----------------------------------------------------------------------------
=head2 getConfig ( )
Get a hashref of configuration to create this MCE. You must run the code
from getLoadPlugins before you can successfully initialize an MCE. You
must also specify the "elements" key so TinyMCE knows what textarea to
replace.
=cut
sub getConfig {
my ($self) = @_;
my $i18n = WebGUI::International->new($self->session, 'Asset_RichEdit');
my @plugins;
push @plugins, "safari";
push @plugins, "paste";
push @plugins, "contextmenu"
if $self->enableContextMenu;
push @plugins, "inlinepopups"
if $self->inlinePopups;
push @plugins, "media"
if $self->allowMedia;
my @toolbarRows = map{[split "\n", $self->get("toolbarRow$_")]} (1..3);
my @toolbarButtons = map{ @{$_} } @toolbarRows;
my %config = (
mode => 'exact',
theme => "advanced",
relative_urls => JSON::false(),
remove_script_host => JSON::true(),
auto_reset_designmode => JSON::true(),
cleanup_callback => "tinyMCE_WebGUI_Cleanup",
urlconverter_callback => "tinyMCE_WebGUI_URLConvertor",
theme_advanced_resizing => JSON::true(),
( map { "theme_advanced_buttons" . ( $_ + 1 ) => ( join ',', @{ $toolbarRows[$_] } ) } ( 0 .. $#toolbarRows ) ),
ask => JSON::false(),
preformatted => $self->preformatted ? JSON::true() : JSON::false(),
force_br_newlines => $self->useBr ? JSON::true() : JSON::false(),
force_p_newlines => $self->useBr ? JSON::false() : JSON::true(),
$self->useBr ? ( forced_root_block => JSON::false() ) : (),
remove_linebreaks => $self->removeLineBreaks ? JSON::true() : JSON::false(),
nowrap => $self->nowrap ? JSON::true() : JSON::false(),
directionality => $self->directionality,
theme_advanced_toolbar_location => $self->toolbarLocation,
theme_advanced_statusbar_location => "bottom",
valid_elements => $self->validElements,
wg_userIsVisitor => $self->session->user->isVisitor ? JSON::true() : JSON::false(),
paste_postprocess => 'tinyMCE_WebGUI_paste_postprocess',
);
foreach my $button (@toolbarButtons) {
if ( $button eq "spellchecker" && $self->session->config->get('availableDictionaries') ) {
push( @plugins, "-wgspellchecker" );
$config{spellchecker_rpc_url} = $self->session->url->gateway( '', "op=spellCheck" );
$config{spellchecker_languages} = join( ',',
map { ( $_->{default} ? '+' : '' ) . $_->{name} . '=' . $_->{id} }
@{ $self->session->config->get('availableDictionaries') } );
}
push( @plugins, "table" ) if ( $button eq "tablecontrols" );
push( @plugins, "save" ) if ( $button eq "save" );
push( @plugins, "advhr" ) if ( $button eq "advhr" );
push( @plugins, "fullscreen" ) if ( $button eq "fullscreen" );
if ( $button eq "advimage" ) {
push( @plugins, "advimage" );
$config{external_link_list_url} = "";
}
if ( $button eq "advlink" ) {
$config{external_image_list_url} = "";
$config{file_browser_callback} = "mcFileManager.filebrowserCallBack";
push( @plugins, "advlink" );
}
push( @plugins, "emotions" ) if ( $button eq "emotions" );
push( @plugins, "iespell" ) if ( $button eq "iespell" );
$config{gecko_spellcheck} = 'true' if ( $button eq "iespell" );
if ( $button eq "paste" || $button eq "pastetext" || $button eq "pasteword" ) {
push( @plugins, "paste" );
}
if ( $button eq "insertdate" || $button eq "inserttime" || $button eq "insertdatetime" ) {
$config{plugin_insertdate_dateFormat} = "%Y-%m-%d";
$config{plugin_insertdate_timeFormat} = "%H:%M:%S";
push( @plugins, "insertdatetime" );
}
push( @plugins, "preview" ) if ( $button eq "preview" );
if ( $button eq "media" ) {
push( @plugins, "media" );
}
push( @plugins, "searchreplace" )
if ( $button eq "search" || $button eq "replace" || $button eq "searchreplace" );
push( @plugins, "print" ) if ( $button eq "print" );
if ( $button eq "wginsertimage" ) {
push @plugins, "-wginsertimage";
}
if ( $button eq "wgpagetree" ) {
push @plugins, "-wgpagetree";
}
if ( $button eq "wgmacro" ) {
push @plugins, "-wgmacro";
}
if ( $button eq "code" ) {
$config{theme_advanced_source_editor_width} = $self->sourceEditorWidth
if ( $self->sourceEditorWidth > 0 );
$config{theme_advanced_source_editor_height} = $self->sourceEditorHeight
if ( $self->sourceEditorHeight > 0 );
}
} ## end foreach my $button (@toolbarButtons)
my $language = $i18n->getLanguage( '', "languageAbbreviation" );
unless ($language) {
$language = $i18n->getLanguage( "English", "languageAbbreviation" );
}
$config{language} = $language;
$config{content_css} = $self->cssFile
|| $self->session->url->extras('tinymce-webgui/defaultcontent.css');
$config{width} = $self->editorWidth if ( $self->editorWidth > 0 );
$config{height} = $self->editorHeight if ( $self->editorHeight > 0 );
$config{plugins} = join( ",", @plugins );
return \%config;
} ## end sub getConfig
#-------------------------------------------------------------------
@ -354,6 +487,40 @@ my $sql = "select asset.assetId, assetData.revisionDate from RichEdit left join
#-------------------------------------------------------------------
=head2 getLoadPlugins ( )
Get the JS code to load the plugins for this MCE. Needs to be called once
on the page this MCE will be on
=cut
sub getLoadPlugins {
my ( $self ) = @_;
my %loadPlugins;
for my $button ( $self->getAllButtons ) {
if ( $button eq 'spellchecker' ) {
$loadPlugins{wgspellchecker} = $self->session->url->extras("tinymce-webgui/plugins/wgspellchecker/editor_plugin.js");
}
if ( $button eq 'wginsertimage' ) {
$loadPlugins{wginsertimage} = $self->session->url->extras("tinymce-webgui/plugins/wginsertimage/editor_plugin.js");
}
if ( $button eq 'wgpagetree' ) {
$loadPlugins{wgpagetree} = $self->session->url->extras("tinymce-webgui/plugins/wgpagetree/editor_plugin.js");
}
if ( $button eq 'wgmacro' ) {
$loadPlugins{wgmacro} = $self->session->url->extras("tinymce-webgui/plugins/wgmacro/editor_plugin.js");
}
}
my $out = '';
while (my ($plugin, $path) = each %loadPlugins) {
$out .= "tinymce.PluginManager.load('$plugin', '$path');\n";
}
return $out;
}
#-------------------------------------------------------------------
=head2 getRichEditor ( $nameId )
Return the javascript needed to make the Rich Editor.
@ -369,119 +536,16 @@ sub getRichEditor {
my $self = shift;
return '' if ($self->disableRichEditor);
my $nameId = shift;
my @plugins;
my %loadPlugins;
push @plugins, "safari";
push @plugins, "contextmenu"
if $self->enableContextMenu;
push @plugins, "inlinepopups"
if $self->inlinePopups;
push @plugins, "media"
if $self->allowMedia;
my @toolbarRows = map{[split "\n", $self->get("toolbarRow$_")]} (1..3);
my @toolbarButtons = map{ @{$_} } @toolbarRows;
my $i18n = WebGUI::International->new($self->session, 'Asset_RichEdit');
my $ask = $self->askAboutRichEdit;
my %config = (
mode => $ask ? "none" : "exact",
elements => $nameId,
theme => "advanced",
relative_urls => JSON::false(),
remove_script_host => JSON::true(),
auto_reset_designmode => JSON::true(),
cleanup_callback => "tinyMCE_WebGUI_Cleanup",
urlconverter_callback => "tinyMCE_WebGUI_URLConvertor",
theme_advanced_resizing => JSON::true(),
(map { "theme_advanced_buttons".($_+1) => (join ',', @{$toolbarRows[$_]}) }
(0..$#toolbarRows)),
#ask => $self->getValue("askAboutRichEdit") ? JSON::true() : JSON::false(),
ask => JSON::false(),
preformatted => $self->preformatted ? JSON::true() : JSON::false(),
force_br_newlines => $self->useBr ? JSON::true() : JSON::false(),
force_p_newlines => $self->useBr ? JSON::false() : JSON::true(),
$self->useBr ? ( forced_root_block => JSON::false() ) : (),
remove_linebreaks => $self->removeLineBreaks ? JSON::true() : JSON::false(),
nowrap => $self->nowrap ? JSON::true() : JSON::false(),
directionality => $self->directionality,
theme_advanced_toolbar_location => $self->toolbarLocation,
theme_advanced_statusbar_location => "bottom",
valid_elements => $self->validElements,
wg_userIsVisitor => $self->session->user->isVisitor ? JSON::true() : JSON::false(),
);
# if ($ask) {
# $config{oninit} = 'turnOffTinyMCE_'.$nameId;
# }
foreach my $button (@toolbarButtons) {
if ($button eq "spellchecker" && $self->session->config->get('availableDictionaries')) {
push(@plugins,"-wgspellchecker");
$loadPlugins{wgspellchecker} = $self->session->url->extras("tinymce-webgui/plugins/wgspellchecker/editor_plugin.js");
$config{spellchecker_rpc_url} = $self->session->url->gateway('', "op=spellCheck");
$config{spellchecker_languages} =
join(',', map { ($_->{default} ? '+' : '').$_->{name}.'='.$_->{id} } @{$self->session->config->get('availableDictionaries')});
}
push(@plugins,"table") if ($button eq "tablecontrols");
push(@plugins,"save") if ($button eq "save");
push(@plugins,"advhr") if ($button eq "advhr");
push(@plugins,"fullscreen") if ($button eq "fullscreen");
if ($button eq "advimage") {
push(@plugins,"advimage");
$config{external_link_list_url} = "";
}
if ($button eq "advlink") {
$config{external_image_list_url} = "";
$config{file_browser_callback} = "mcFileManager.filebrowserCallBack";
push(@plugins,"advlink");
}
push(@plugins,"emotions") if ($button eq "emotions");
push(@plugins,"iespell") if ($button eq "iespell");
$config{gecko_spellcheck} = 'true' if ($button eq "iespell");
if ($button eq "paste" || $button eq "pastetext" || $button eq "pasteword") {
push(@plugins,"paste");
}
if ($button eq "insertdate" || $button eq "inserttime" || $button eq "insertdatetime") {
$config{plugin_insertdate_dateFormat} = "%Y-%m-%d";
$config{plugin_insertdate_timeFormat} = "%H:%M:%S";
push(@plugins,"insertdatetime");
}
push(@plugins,"preview") if ($button eq "preview");
if ($button eq "media") {
push(@plugins,"media");
}
push(@plugins,"searchreplace") if ($button eq "search" || $button eq "replace" || $button eq "searchreplace");
push(@plugins,"print") if ($button eq "print");
if ($button eq "wginsertimage") {
push @plugins, "-wginsertimage";
$loadPlugins{wginsertimage} = $self->session->url->extras("tinymce-webgui/plugins/wginsertimage/editor_plugin.js");
}
if ($button eq "wgpagetree") {
push @plugins, "-wgpagetree";
$loadPlugins{wgpagetree} = $self->session->url->extras("tinymce-webgui/plugins/wgpagetree/editor_plugin.js");
}
if ($button eq "wgmacro") {
push @plugins, "-wgmacro";
$loadPlugins{wgmacro} = $self->session->url->extras("tinymce-webgui/plugins/wgmacro/editor_plugin.js");
}
if ($button eq "code") {
$config{theme_advanced_source_editor_width} = $self->sourceEditorWidth if ($self->sourceEditorWidth > 0);
$config{theme_advanced_source_editor_height} = $self->sourceEditorHeight if ($self->sourceEditorHeight > 0);
}
}
my $language = $i18n->getLanguage('' ,"languageAbbreviation");
unless ($language) {
$language = $i18n->getLanguage("English","languageAbbreviation");
}
$config{language} = $language;
$config{content_css} = $self->cssFile || $self->session->url->extras('tinymce-webgui/defaultcontent.css');
$config{width} = $self->editorWidth || "100%";
$config{height} = $self->editorHeight || "100%";
$config{plugins} = join(",",@plugins);
$self->richedit_headTags;
my $out = '';
if ($ask) {
$out = q|<a style="display: block;" href="javascript:toggleEditor('|.$nameId.q|')">|.$i18n->get('Toggle editor').q|</a>|;
}
$self->richedit_headTags;
$out .= qq|<script type="text/javascript">\n|;
if ($ask) {
$out .= <<"EOHTML1";
@ -498,11 +562,16 @@ EOHTML1
# }
#}
#YAHOO.util.Event.onDOMReady(turnOffTinyMCE_$nameId);
}
my $config = $self->getConfig;
$config->{ elements } = $nameId;
if ( $ask ) {
$config->{mode} = "none";
}
while (my ($plugin, $path) = each %loadPlugins) {
$out .= "tinymce.PluginManager.load('$plugin', '$path');\n";
}
$out .= "\ttinyMCE.init(" . JSON->new->pretty->encode(\%config) . " );\n";
$out .= $self->getLoadPlugins;
$out .= "\ttinyMCE.init(" . JSON->new->pretty->encode( $config ) . " );\n";
$out .= "</script>";
}

View file

@ -637,7 +637,8 @@ sub getShortcutByCriteria {
my $replacement = $expression; # We don't want to modify $expression.
# We need it later.
push(@joins," left join metaData_values ".$counter."_v on a.assetId=".$counter."_v.assetId ");
my $alias = $counter . '_v';
push(@joins," left join metaData_values $alias on a.assetId=$alias.assetId and d.revisionDate = $alias.revisionDate ");
# Get the field (State) and the value (Wisconsin) from the $expression.
$expression =~ /($attribute)\s*($operator)\s*($attribute)/gi;
my $field = $1;
@ -670,7 +671,12 @@ sub getShortcutByCriteria {
}
my $sql = "select a.assetId from asset a
".join("\n", @joins)."
left join assetData d on a.assetId = d.assetId
and d.revisionDate=(
select max(revisionDate)
from assetData d2
where d2.assetId = a.assetId
) ".join("\n", @joins)."
where a.className = ".$db->quote($self->getShortcutDefault->get("className"));
# Add constraint only if it has been modified.
$sql .= " and ".$constraint if (($constraint ne $criteria) && $constraint ne "");

View file

@ -528,7 +528,9 @@ The WebGUI::Shop::TransactionItem being refunded.
sub onRefund {
my ($self, $item) = @_;
$self->onCancelRecurring($item);
if ($self->isRecurring) {
$self->onCancelRecurring($item);
}
return undef;
}

View file

@ -1792,34 +1792,42 @@ sub view {
$var{'addaccessory_url'} = $self->getUrl('func=addAccessory');
$var{'addaccessory_label'} = $i18n->get(36);
##Need an id for collateral operations, and an assetId for asset instantiation.
foreach my $collateral ( @{ $self->getAllCollateral('accessoryJSON') } ) {
ACCESSORY: foreach my $collateral ( @{ $self->getAllCollateral('accessoryJSON') } ) {
my $id = $collateral->{accessoryAssetId};
$segment = $self->session->icon->delete('func=deleteAccessoryConfirm&aid='.$id,$self->get('url'),$i18n->get(2))
. $self->session->icon->moveUp('func=moveAccessoryUp&aid='.$id,$self->get('url'))
. $self->session->icon->moveDown('func=moveAccessoryDown&aid='.$id,$self->get('url'));
my $accessory_vars = {
accessory_title => $i18n->get('Lost'),
accessory_controls => $segment,
};
my $accessory = WebGUI::Asset->newById($session, $collateral->{accessoryAssetId});
push(@accessoryloop,{
'accessory_URL' => $accessory->getUrl,
'accessory_title' => $accessory->getTitle,
'accessory_controls' => $segment,
});
if ( $accessory ) {
$accessory_vars->{ accessory_URL } = $accessory->getUrl;
$accessory_vars->{ accessory_title } = $accessory->getTitle;
}
push(@accessoryloop, $accessory_vars);
}
$var{accessory_loop} = \@accessoryloop;
#---related
$var{'addrelatedproduct_url'} = $self->getUrl('func=addRelated');
$var{'addrelatedproduct_label'} = $i18n->get(37);
foreach my $collateral ( @{ $self->getAllCollateral('relatedJSON')} ) {
RELATED: foreach my $collateral ( @{ $self->getAllCollateral('relatedJSON')} ) {
my $id = $collateral->{relatedAssetId};
$segment = $self->session->icon->delete('func=deleteRelatedConfirm&rid='.$id, $self->get('url'),$i18n->get(4))
. $self->session->icon->moveUp('func=moveRelatedUp&rid='.$id, $self->get('url'))
. $self->session->icon->moveDown('func=moveRelatedDown&rid='.$id, $self->get('url'));
my $related_vars = {
relatedproduct_title => $i18n->get('Lost'),
relatedproduct_controls => $segment,
};
my $related = WebGUI::Asset->newById($session, $collateral->{relatedAssetId});
push(@relatedloop,{
'relatedproduct_URL' => $related->getUrl,
'relatedproduct_title' => $related->getTitle,
'relatedproduct_controls' => $segment,
});
if ($related) {
$related_vars->{ relatedproduct_URL } = $related->getUrl;
$related_vars->{ relatedproduct_title } = $related->getTitle;
}
push(@relatedloop, $related_vars);
}
$var{relatedproduct_loop} = \@relatedloop;

View file

@ -91,13 +91,6 @@ property cacheTimeout => (
label => ["cache timeout",'Asset_Snippet'],
hoverHelp => ["cache timeout help",'Asset_Snippet'],
);
property processAsTemplate => (
fieldType => 'yesNo',
label => ['process as template','Asset_Snippet'],
hoverHelp => ['process as template description','Asset_Snippet'],
tab => "properties",
default => 0,
);
property mimeType => (
tab => "properties",
hoverHelp => ['mimeType description','Asset_Snippet'],
@ -105,6 +98,15 @@ property mimeType => (
fieldType => 'mimeType',
default => 'text/html',
);
property templateParser => (
fieldType => 'templateParser',
allowNone => 1,
label => ['parser','Asset_Template'],
hoverHelp => ['parser description','Asset_Template'],
tab => 'properties',
defaultValue => '',
);
has '+uiLevel' => (
default => 5,
);
@ -247,13 +249,15 @@ sub view {
my $out = $session->cache->get( $cacheKey );
return $out if $out;
}
my $output = $self->usePacked
? $self->snippetPacked
: $self->snippet
;
my $output = $self->get('usePacked')
? $self->get("snippetPacked")
: $self->get('snippet')
;
$output = $self->getToolbar.$output if ($session->isAdminOn && !$calledAsWebMethod);
if ($self->processAsTemplate) {
$output = WebGUI::Asset::Template->processRaw($session, $output, $self->get);
if (my $parser = $self->templateParser) {
$output = WebGUI::Asset::Template->processRaw(
$session, $output, $self->get, $parser
);
}
WebGUI::Macro::process($session,\$output);
unless ($noCache) {

View file

@ -207,6 +207,26 @@ sub exportAssetData {
#-------------------------------------------------------------------
=head2 exportGetRelatedAssetIds
Overriden to include any topics in which this story would appear.
=cut
sub exportGetRelatedAssetIds {
my $self = shift;
my $rel = $self->SUPER::exportGetRelatedAssetIds(@_);
push @$rel, @{
WebGUI::Keyword->new($self->session)->getMatchingAssets({
keywords => WebGUI::Keyword::string2list($self->get('keywords')),
isa => 'WebGUI::Asset::Wobject::StoryTopic',
})
};
return $rel;
}
#-------------------------------------------------------------------
=head2 formatDuration ( $lastUpdated )
Format the time since this story was last updated. If it is longer than 1 week, then
@ -396,6 +416,9 @@ sub getEditForm {
value => $i18n->get('save and add another photo'),
}),
};
if ($session->setting->get('metaDataEnabled')) {
$var->{metadata} = $self->getMetaDataAsFormFields;
}
$var->{ photo_form_loop } = [];
##Provide forms for the existing photos, if any
##Existing photos get a delete Yes/No.
@ -405,12 +428,16 @@ sub getEditForm {
foreach my $photoIndex (1..$numberOfPhotos) {
my $photo = $photoData->[$photoIndex-1];
my $storage = WebGUI::Storage->get($session, $photo->{storageId});
my $filename = $storage->getFiles->[0];
my $filename = $storage && $storage->getFiles->[0];
push @{ $var->{ photo_form_loop } }, {
hasPhoto => $filename ? 1 : 0,
imgThumb => $filename ? $storage->getThumbnailUrl($filename) : '',
imgUrl => $filename ? $storage->getUrl($filename) : '',
imgFilename => $filename ? $filename : '',
imgRemoteUrlForm => WebGUI::Form::text($session, {
name => 'imgRemoteUrl'.$photoIndex,
value => $photo->{remoteUrl},
}),
newUploadForm => WebGUI::Form::file($session, {
name => 'newPhoto' . $photoIndex,
maxAttachments => 1,
@ -442,6 +469,9 @@ sub getEditForm {
};
}
push @{ $var->{ photo_form_loop } }, {
imgRemoteUrlForm => WebGUI::Form::text($session, {
name => 'imgRemoteUrl',
}),
newUploadForm => WebGUI::Form::image($session, {
name => 'newPhoto',
maxAttachments => 1,
@ -580,16 +610,20 @@ sub processEditForm {
PHOTO: foreach my $photoIndex (1..$numberOfPhotos) {
##TODO: Deletion check and storage cleanup
my $storageId = $photoData->[$photoIndex-1]->{storageId};
my $storage = $storageId && WebGUI::Storage->get($session, $storageId);
my $remote = $form->process("imgRemoteUrl$photoIndex");
if ($form->process('deletePhoto'.$photoIndex, 'yesNo')) {
my $storage = WebGUI::Storage->get($session, $storageId);
$storage->delete if $storage;
splice @{ $photoData }, $photoIndex-1, 1;
next PHOTO;
}
##Process photos with urls that replace existing photos
if ($remote) {
$storage->delete() if $storage;
}
##Process uploads that replace existing photos
if (my $uploadId = $form->process('newPhoto'.$photoIndex,'File')) {
elsif (my $uploadId = $form->process('newPhoto'.$photoIndex,'File')) {
my $upload = WebGUI::Storage->get($session, $uploadId);
my $storage = WebGUI::Storage->get($session, $storageId);
$storage->clear;
my $filename = $upload->getFiles->[0];
$storage->addFileFromFilesystem($upload->getPath($filename));
@ -600,31 +634,43 @@ sub processEditForm {
$upload->delete;
}
my $newPhoto = {
storageId => $storageId,
caption => $form->process('imgCaption'.$photoIndex, 'text'),
alt => $form->process('imgAlt' .$photoIndex, 'text'),
title => $form->process('imgTitle' .$photoIndex, 'text'),
byLine => $form->process('imgByline' .$photoIndex, 'text'),
url => $form->process('imgUrl' .$photoIndex, 'url' ),
};
if ($remote) {
$newPhoto->{remoteUrl} = $remote;
}
else {
$newPhoto->{storageId} = $storageId;
}
splice @{ $photoData }, $photoIndex-1, 1, $newPhoto;
}
my $newStorageId = $form->process('newPhoto', 'image');
if ($newStorageId) {
my $newStorage = WebGUI::Storage->get($session, $newStorageId);
my $photoName = $newStorage->getFiles->[0];
my ($width, $height) = $newStorage->getSizeInPixels($photoName);
if ($width > $self->getArchive->photoWidth) {
$newStorage->resize($photoName, $self->getArchive->photoWidth);
}
push @{ $photoData }, {
my $newRemote = $form->process('imgRemoteUrl');
if ($newStorageId || $newRemote) {
my $newPhoto = {
caption => $form->process('newImgCaption', 'text'),
alt => $form->process('newImgAlt', 'text'),
title => $form->process('newImgTitle', 'text'),
byLine => $form->process('newImgByline', 'text'),
url => $form->process('newImgUrl', 'url'),
storageId => $newStorageId,
};
if ($newRemote) {
$newPhoto->{remoteUrl} = $newRemote;
}
else {
my $newStorage = WebGUI::Storage->get($session, $newStorageId);
my $photoName = $newStorage->getFiles->[0];
my ($width, $height) = $newStorage->getSizeInPixels($photoName);
if ($width > $self->getArchive->get('photoWidth')) {
$newStorage->resize($photoName, $self->getArchive->get('photoWidth'));
}
$newPhoto->{storageId} = $newStorageId;
}
push @{ $photoData }, $newPhoto;
}
$self->setPhotoData($photoData);
$self->{_parent} = $archive; ##Restore archive, for URL and other calculations
@ -666,8 +712,9 @@ Remove the storage locations for this revision of the Asset.
override purgeRevision => sub {
my $self = shift;
my $session = $self->session;
foreach my $photo ( @{ $self->getPhotoData} ) {
my $storage = WebGUI::Storage->get($session, $self-$photo->{storageId});
PHOTO: foreach my $photo ( @{ $self->getPhotoData} ) {
my $id = $photo->{storageId} or next PHOTO;
my $storage = WebGUI::Storage->get($session, $id);
$storage->delete if $storage;
}
return super();
@ -832,13 +879,23 @@ sub viewTemplateVariables {
}
}
my $key = WebGUI::Keyword->new($session);
my $keywords = $key->getKeywordsForAsset( { asArrayRef => 1, asset => $self });
my $isExporting = $session->scratch->get('isExporting');
my $key = WebGUI::Keyword->new($session);
my $keywords = $key->getKeywordsForAsset( { asArrayRef => 1, asset => $self });
$var->{keyword_loop} = [];
my $parent = $self->getParent;
my $upwards = $parent->isa('WebGUI::Asset::Wobject::StoryArchive')
? '' #In parallel with the Keywords files
: '../' #Keywords files are one level up
;
foreach my $keyword (@{ $keywords }) {
my $keyword_url = $isExporting
? $upwards . $archive->getKeywordFilename($keyword)
: $archive->getUrl("func=view;keyword=".$session->url->escape($keyword))
;
push @{ $var->{keyword_loop} }, {
keyword => $keyword,
url => $archive->getUrl("func=view;keyword=".$session->url->escape($keyword)),
url => $keyword_url,
};
}
$var->{updatedTime} = $self->formatDuration();
@ -849,11 +906,19 @@ sub viewTemplateVariables {
$var->{photo_loop} = [];
my $photoCounter = 0;
PHOTO: foreach my $photo (@{ $photoData }) {
next PHOTO unless $photo->{storageId};
my $storage = WebGUI::Storage->get($session, $photo->{storageId});
my $file = $storage->getFiles->[0];
next PHOTO unless $file;
my $imageUrl = $storage->getUrl($file);
my $imageUrl;
if (my $remote = $photo->{remoteUrl}) {
$imageUrl = $remote;
}
elsif (my $id = $photo->{storageId}) {
my $storage = WebGUI::Storage->get($session, $photo->{storageId});
my $file = $storage->getFiles->[0];
next PHOTO unless $file;
$imageUrl = $storage->getUrl($file);
}
else {
next PHOTO;
}
push @{ $var->{photo_loop} }, {
imageUrl => $imageUrl,
imageCaption => $photo->{caption},

View file

@ -115,7 +115,8 @@ use List::MoreUtils qw{ any };
use Tie::IxHash;
use Clone qw/clone/;
use HTML::Packer;
use JSON qw{ to_json };
use JSON qw{ to_json from_json };
use Try::Tiny;
=head1 NAME
@ -227,6 +228,8 @@ around cut => sub {
return $returnValue;
};
#-------------------------------------------------------------------
=head2 addRevision ( )
Override the master addRevision to copy attachments
@ -324,19 +327,22 @@ Returns the WebGUI::FormBuilder object that will be used in generating the edit
override getEditForm => sub {
my $self = shift;
my $tabform = super();
my $i18n = WebGUI::International->new($self->session, 'Asset_Template');
my $session = $self->session;
my ( $url, $style ) = $session->quick(qw( url style ));
my $i18n = WebGUI::International->new($session, 'Asset_Template');
my $returnUrl = $session->form->get("returnUrl");
$tabform->addField( "hidden",
name=>"returnUrl",
value=>$self->session->form->get("returnUrl")
value=>$returnUrl,
);
if ($self->namespace eq "") {
my $namespaces = $self->session->dbSlave->buildHashRef("select distinct(namespace) from template order by namespace");
my $namespaces = $session->dbSlave->buildHashRef("select distinct(namespace) from template order by namespace");
$tabform->getTab("properties")->addField( "combo",
name=>"namespace",
options=>$namespaces,
label=>$i18n->get('namespace'),
hoverHelp=>$i18n->get('namespace description'),
value=>[$self->session->form->get("namespace")]
value=>[$session->form->get("namespace")]
);
} else {
$tabform->getTab("meta")->addField( "ReadOnly",
@ -346,14 +352,68 @@ override getEditForm => sub {
value=>$self->namespace
);
}
if($self->session->config->get("templateParsers")){
my @temparray = @{$self->session->config->get("templateParsers")};
my $previewButtons
= $tabform->getTab('properties')->addField( "ButtonGroup",
name => 'previewButtons',
label => $i18n->get('Preview'),
);
$previewButtons->addButton( 'Button' => { id => 'preview', value => $i18n->get('Preview') } );
$previewButtons->addButton( 'Button' => { id => 'previewConfig', value => $i18n->get('Configure') } );
my $cform = WebGUI::HTMLForm->new($session);
$cform->yesNo(
id => 'previewRaw',
name => 'previewRaw',
label => $i18n->get('Plain Text?'),
hoverHelp => $i18n->get('Plain Text hoverHelp'),
);
$cform->text(
id => 'previewFetchUrl',
label => $i18n->get('URL'),
hoverHelp => $i18n->get('URL hoverHelp'),
defaultValue => $returnUrl,
);
$cform->button(
id => 'previewFetch',
label => $i18n->get('Fetch Variables'),
hoverHelp => $i18n->get('Fetch Variables hoverHelp'),
value => $i18n->get('Fetch'),
);
$cform->codearea(
id => 'previewVars',
label => $i18n->get('Variables'),
hoverHelp => $i18n->get('Variables hoverHelp'),
);
$cform->hidden(id => 'previewId', value => $self->getId);
$cform->hidden(id => 'previewGateway', value => $url->gateway);
$tabform->getTab('properties')->addField("ReadOnly",
name => 'previewDialog',
value => qq(
<div id='previewConfigForm'>
<div class='hd'>${\ $i18n->get('Configure Preview') }</div>
<table class='bd'>${\ $cform->printRowsOnly }</table>
<div class='ft' style='margin:0 auto; text-align: center'>
<button id='previewConfigClose'>Close</button>
</div>
</div>
),
);
$style->setScript($url->extras($_)) for qw(
yui/build/json/json-min.js
yui/build/container/container-min.js
templatePreview.js
);
if($session->config->get("templateParsers")){
my @temparray = @{$session->config->get("templateParsers")};
tie my %parsers, 'Tie::IxHash';
while(my $a = shift @temparray){
$parsers{$a} = $self->getParser($self->session, $a)->getName();
$parsers{$a} = $self->getParser($session, $a)->getName();
}
my $value = [$self->parser];
$value = \[$self->session->config->get("defaultTemplateParser")] if(!$self->parser);
$value = \[$session->config->get("defaultTemplateParser")] if(!$self->parser);
$tabform->getTab("properties")->addField( "SelectBox",
name=>"parser",
options=>\%parsers,
@ -501,6 +561,22 @@ sub getParser {
return $parser->new($session);
}
#-------------------------------------------------------------------
#
# See the warning about using this on processVariableHeaders(). If no
# variables were captured, we'll return the empty string.
sub getVariableJson {
my ($class, $session) = @_;
my ($show, $vars, $json);
return ($show = $session->stow->get('showTemplateVars'))
&& ($vars = $show->{vars})
&& ($json = eval { JSON::encode_json($vars) })
&& ($show->{startDelimiter} . $json . $show->{endDelimiter})
or '';
}
#-------------------------------------------------------------------
=head2 importAssetCollateralData ( data )
@ -631,6 +707,15 @@ sub process {
return to_json( $self->param );
}
my $stow = $session->stow;
my $show = $stow->get('showTemplateVars');
if ( $show && $show->{assetId} eq $self->getId && $self->canEdit ) {
# This will never be true again, cause we're getting rid of assetId
delete $show->{assetId};
$show->{vars} = $vars;
$stow->set( showTemplateVars => $show );
}
$self->prepare unless ($self->{_prepared});
my $parser = $self->getParser($session, $self->parser);
my $template = $self->usePacked
@ -653,6 +738,59 @@ sub process {
return $output;
}
#-------------------------------------------------------------------
# Used for debugging and the template test renderer.
# WARNING: Please do not rely on this behavior. It's a bit of a hack, and
# should not be considered part of the core API. Eventually, we will have
# introspectable template objects so that you can more easily (and
# efficiently) get this kind of information.
# If the first value for the 'X-Webgui-Template-Variables' header is our
# assetId, then in addition to processing the template, append add a json
# representation of our template variables to the response. The headers
# "X-Webgui-Template-Variables-Start" and "X-Webgui-Template-Variables-End"
# will contain the delimiters for the start and end of this content so that
# the user agent (who had to have stuck the header in in the first place) can
# parse it out. The delimiters will make the whole thing look like an xml
# comment (<!-- ... -->) just in case.
# We would just send the vars in the header, but different webservers have
# different limits on header field size and it's impossible to say whether our
# data will fit inside them or not.
# This is intended to be called earlier in the request cycle (in the Content
# URL handler) so that the headers get sent before any chunked content starts
# being set up. We set the stow here and check it during process() to see
# whether we need to include the delimited json. Later on, Content will call
# call getVariableJson to get the results.
{
my $head = 'X-Webgui-Template-Variables';
my @chr = ('0'..'9', 'a'..'z', 'A'..'Z');
sub processVariableHeaders {
my ($class, $session) = @_;
my $r = $session->request;
if (my $id = $r->headers->header($head)) {
my $rnd = join('', map { $chr[int(rand($#chr))] } (1..32));
my $out = {};
my $st = "<!-- $rnd ";
my $end = " $rnd -->";
$out->{"$head-Start"} = $st;
$out->{"$head-End"} = $end;
$session->response->headers( $out );
$session->stow->set(
showTemplateVars => {
assetId => $id,
startDelimiter => $st,
endDelimiter => $end,
}
);
}
}
}
#-------------------------------------------------------------------
@ -921,6 +1059,42 @@ sub www_manage {
#-------------------------------------------------------------------
=head2 www_preview
Rendes this template with the given variables (posted as JSON)
=cut
sub www_preview {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient unless $self->canEdit;
my $form = $session->form;
my $http = $session->http;
try {
my $output = $self->processRaw(
$session,
$form->get('template'),
from_json($form->get('variables')),
$form->get('parser'),
);
if ($form->get('plainText')) {
$http->setMimeType('text/plain');
}
elsif ($output !~ /<html>/) {
$output = $session->style->userStyle($output);
}
return $output;
} catch {
$http->setMimeType('text/plain');
$_[0];
}
}
#-------------------------------------------------------------------
=head2 www_view
Override the default behavior. When a template is viewed, it redirects you

View file

@ -17,6 +17,7 @@ package WebGUI::Asset::Template::TemplateToolkit;
use strict;
use base 'WebGUI::Asset::Template::Parser';
use Template;
use WebGUI::Template::Provider;
#-------------------------------------------------------------------
sub _rewriteVars { # replace dots with underscrores in keys (except in keys that aren't usable as variables (URLs etc.))
@ -81,12 +82,30 @@ sub process {
my $vars = $self->addSessionVars(shift);
my ($t,$output);
eval {
$t = Template->new({
INTERPOLATE => 1, # expand "$var" in plain text
POST_CHOMP => 1, # cleanup whitespace
EVAL_PERL => 0, # evaluate Perl code blocks
});
$t->process( \$template, _rewriteVars($vars),\$output) || $self->session->log->error($t->error());
my $config = $self->session->config->get( 'template' ) || {};
$config->{INTERPOLATE} //= 1; # expand "$var" in plain text
$config->{POST_CHOMP} //= 1; # cleanup whitespace
$config->{EVAL_PERL} //= 0; # evaluate Perl code blocks
# Add WebGUI::Template::Plugin to PLUGIN_BASE
if ( defined $config->{PLUGIN_BASE} && !ref $config->{PLUGIN_BASE} ) {
$config->{PLUGIN_BASE} = [ $config->{PLUGIN_BASE} ];
}
elsif ( !defined $config->{PLUGIN_BASE} ) {
$config->{PLUGIN_BASE} = [];
}
push @{$config->{PLUGIN_BASE}}, 'WebGUI::Template::Plugin';
# Allow WebGUI assets to be included in templates
$config->{LOAD_TEMPLATES} = [ WebGUI::Template::Provider->new( $self->session, $config ) ];
$t = Template->new( $config );
$vars = _rewriteVars($vars);
$vars->{_session} = $self->session;
unless ($t->process( \$template, $vars, \$output)) {
my $e = $t->error;
$self->session->log->error($e);
die $e;
}
};
if ($@) {
WebGUI::Error::Template->throw( error => $@ );

View file

@ -17,6 +17,7 @@ use Moose;
use Tie::IxHash;
use WebGUI::Definition::Asset;
extends 'WebGUI::Asset::Wobject';
with 'WebGUI::Role::Asset::JSONCollateral';
define assetName => ['assetName', 'Asset_Calendar'];
define icon => 'calendar.gif';
@ -260,7 +261,7 @@ property icalFeeds => (
label => ['Feed URL','Asset_Calendar'],
},
{
name => 'status',
name => 'lastResult',
type => 'readonly',
label => ['434','WebGUI'],
},
@ -303,6 +304,7 @@ use WebGUI::Search;
use WebGUI::Form;
use WebGUI::HTML;
use WebGUI::DateTime;
use WebGUI::ICal;
use DateTime;
use JSON;
@ -348,10 +350,6 @@ The date this feed was added, or edited last.
The results of what happened the last time this feed was accessed to pull iCal.
=head4 feedType
What kind of feed this is.
=cut
sub addFeed {
@ -860,6 +858,17 @@ override processEditForm => sub {
$self->createSubscriptionGroup();
}
my @feeds = @{ $self->getFeeds };
foreach my $feed (@feeds) {
if ($feed->{lastUpdated} eq 'new') {
$feed->{lastUpdated} = 'never';
}
if ($feed->{lastResult} eq 'new') {
$feed->{lastResult} = '';
}
$self->setFeed($feed->{feedId}, $feed);
}
return;
};
@ -1706,97 +1715,20 @@ sub www_ical {
$dt_end = $dt_start->clone->add( seconds => $self->icalInterval );
}
my $ical = WebGUI::ICal->new();
# Get all the events we're going to display
my @events = $self->getEventsIn($dt_start->toMysql,$dt_end->toMysql);
my $ical = qq{BEGIN:VCALENDAR\r\n}
. qq{PRODID:WebGUI }.$WebGUI::VERSION."-".$WebGUI::STATUS.qq{\r\n}
. qq{VERSION:2.0\r\n};
# VEVENT:
EVENT: for my $event (@events) {
next EVENT unless $event->canView();
$ical .= qq{BEGIN:VEVENT\r\n};
### UID
# Use feed's UID to prevent over-propagation
if ($event->feedUid) {
$ical .= qq{UID:}.$event->feedUid."\r\n";
}
# Create a UID for feeds native to this calendar
else {
my $domain = $session->config->get("sitename")->[0];
$ical .= qq{UID:}.$event->assetId.'@'.$domain."\r\n";
}
# LAST-MODIFIED (revisionDate)
$ical .= qq{LAST-MODIFIED:}
. WebGUI::DateTime->new($self->session, $event->revisionDate)->toIcal
. "\r\n";
# CREATED (creationDate)
$ical .= qq{CREATED:}
. WebGUI::DateTime->new($self->session, $event->creationDate)->toIcal
. "\r\n";
# SEQUENCE
my $sequenceNumber = $event->iCalSequenceNumber;
if (defined $sequenceNumber) {
$ical .= qq{SEQUENCE:}
. $event->iCalSequenceNumber
. "\r\n";
}
# DTSTART
my $eventStart = $event->getIcalStart;
$ical .= 'DTSTART';
if ($eventStart !~ /T/) {
$ical .= ';VALUE=DATE';
}
$ical .= ":$eventStart\r\n";
# DTEND
my $eventEnd = $event->getIcalEnd;
$ical .= 'DTEND';
if ($eventEnd !~ /T/) {
$ical .= ';VALUE=DATE';
}
$ical .= ":$eventEnd\r\n";
# Summary (the title)
# Wrapped at 75 columns
$ical .= $self->wrapIcal("SUMMARY:".$event->title)."\r\n";
# Description (the text)
# Wrapped at 75 columns
$ical .= $self->wrapIcal("DESCRIPTION:".$event->description)."\r\n";
# Location (the text)
# Wrapped at 75 columns
$ical .= $self->wrapIcal("LOCATION:".$event->location)."\r\n";
# X-WEBGUI lines
if ($event->groupIdView) {
$ical .= "X-WEBGUI-GROUPIDVIEW:".$event->groupIdView."\r\n";
}
if ($event->get("groupIdEdit")) {
$ical .= "X-WEBGUI-GROUPIDEDIT:".$event->groupIdEdit."\r\n";
}
$ical .= "X-WEBGUI-URL:".$event->get("url")."\r\n";
$ical .= "X-WEBGUI-MENUTITLE:".$event->menuTitle."\r\n";
$ical .= qq{END:VEVENT\r\n};
$event->add_to_calendar($ical);
}
# ENDVEVENT
$ical .= qq{END:VCALENDAR\r\n};
# Set mime of text/icalendar
$self->session->response->header( 'Content-Disposition' => qq{attachment; filename="feed.ics"});
$self->session->response->content_type("text/calendar");
return $ical;
#$self->session->http->setMimeType("text/plain");
$self->session->http->setFilename("feed.ics","text/calendar");
return $ical->as_string;
}
#----------------------------------------------------------------------------

View file

@ -48,6 +48,20 @@ property items => (
noFormPost => 1,
fieldType => 'text',
);
property autoPlay => (
fieldType => 'yesNo',
defaultValue => 0,
tab => "properties",
hoverHelp => ['carousel autoPlay description', 'Asset_Carousel' ],
label => ['carousel autoPlay label', 'Asset_Carousel' ],
);
property autoPlayInterval => (
fieldType => 'Integer',
defaultValue => 4,
tab => 'properties',
hoverHelp => ['carousel autoPlayInterval description', 'Asset_Carousel'],
label => ['carousel autoPlayInterval label', 'Asset_Carousel'],
);
#-------------------------------------------------------------------
@ -63,16 +77,30 @@ override getEditForm => sub {
my $tabform = super();
my $i18n = WebGUI::International->new($self->session, "Asset_Carousel");
$self->session->style->setScript($self->session->url->extras('yui/build/editor/editor-min.js'));
$self->session->style->setCss($self->session->url->extras('yui/build/editor/assets/skins/sam/editor.css'));
$self->session->style->setScript($self->session->url->extras('wobject/Carousel/carousel.js'));
$self->session->style->setScript($self->session->url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type =>
'text/javascript'});
$self->session->style->setScript($self->session->url->extras('yui/build/element/element-min.js'), {type =>
'text/javascript'});
$self->session->style->setScript($self->session->url->extras('yui/build/tabview/tabview-min.js'), {type =>
'text/javascript'});
$self->session->style->setScript($self->session->url->extras('yui/build/editor/editor-min.js'), {type =>
'text/javascript'});
$self->session->style->setScript($self->session->url->extras('yui/build/json/json-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->setLink($self->session->url->extras('yui/build/tabview/assets/skins/sam/tabview.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()"></input><br />'
.' <input type="hidden" id="items_formId" name="items" />'
.' <input type="button" value="Add item" onclick="window.carouselEditor.addTab()"></input><br />'
." <br />\n";
$tabform->getTab("properties")->addField('ReadOnly', value => $tableRowStart);
@ -122,6 +150,27 @@ onClick='javascript:deleteItem(this.id)'></input>\n"
."</script>\n";
$tabform->getTab("properties")->addField('ReadOnly', value => $itemHTML);
}
$self->session->log->warn('richedit:' .$self->get('richEditor'));
my $richEditId = $self->get('richEditor') || "PBrichedit000000000001";
my $richedit = WebGUI::Asset->newById( $self->session, $richEditId );
my $config = JSON->new->encode( $richedit->getConfig );
my $loadMcePlugins = $richedit->getLoadPlugins;
my $items = $self->get('items') ? JSON->new->decode($self->get('items'))->{items} : [];
$items = JSON->new->encode( $items );
my $i18nJson = JSON->new->encode( { "delete" => $i18n->get("delete") } );
$tabform->getTab('properties')->raw(<<"ENDHTML");
<div id="carouselEditor"></div>
<script type="text/javascript">
$loadMcePlugins
YAHOO.util.Event.onDOMReady( function() {
window.carouselEditor = new WebGUI.Carousel.Editor( "carouselEditor", $config, $items, $i18nJson );
} );
</script>
ENDHTML
my $tableRowEnd = qq|
</td>
</tr>
@ -165,32 +214,9 @@ Used to process properties from the form posted.
override processEditForm => sub {
my $self = shift;
my $form = $self->session->form;
my (@items,$items);
super();
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});
my $items = JSON->new->decode( $form->get("items") );
$self->update({ items => JSON->new->encode({ items => $items }) });
return undef;
};

View file

@ -879,19 +879,19 @@ override commit => sub {
unless (defined $cron) {
$cron = WebGUI::Workflow::Cron->create($self->session, {
title=>$self->getTitle." ".$i18n->get("mail"),
minuteOfHour=>"*/".($self->getMailInterval/60),
className=>(ref $self),
methodName=>"new",
parameters=>$self->getId,
workflowId=>"csworkflow000000000001"
workflowId=>"csworkflow000000000001",
$self->getCronIntervals,
});
$self->update({getMailCronId=>$cron->getId});
}
if ($self->getMail) {
$cron->set({enabled=>1,title=>$self->getTitle." ".$i18n->get("mail"), minuteOfHour=>"*/".($self->getMailInterval/60)});
} else {
$cron->set({enabled=>0,title=>$self->getTitle." ".$i18n->get("mail"), minuteOfHour=>"*/".($self->getMailInterval/60)});
}
$cron->set({
enabled => $self->get('getMail') ? 1 : 0,
title => $self->getTitle." ".$i18n->get("mail"),
$self->getCronIntervals(),
});
};
#-------------------------------------------------------------------
@ -931,11 +931,11 @@ sub duplicate {
my $i18n = WebGUI::International->new($self->session, "Asset_Collaboration");
my $newCron = WebGUI::Workflow::Cron->create($self->session, {
title=>$self->getTitle." ".$i18n->get("mail"),
minuteOfHour=>"*/".($self->get("getMailInterval")/60),
className=>(ref $self),
methodName=>"new",
parameters=>$self->getId,
workflowId=>"csworkflow000000000001"
workflowId=>"csworkflow000000000001",
$self->getCronIntervals(),
});
$newAsset->update({getMailCronId=>$newCron->getId});
$newAsset->incrementReplies('','');
@ -959,6 +959,87 @@ sub duplicateBranch {
#----------------------------------------------------------------------------
=head2 getCronIntervals
Translate the settings for getCsMailInterval into options for Spectre's Cron,
minuteOfHour, hourOfDay and so on.
Returns a hash of those options that can be folded into a hash of property settings for the
Cron job.
=cut
sub getCronIntervals {
my $self = shift;
my $interval = $self->get('getMailInterval');
##My kingdom for a switch statement!
if ($interval eq 'every minute') {
return(
hourOfDay => '*',
minuteOfHour => '*/1',
);
}
elsif ($interval eq 'every other minute') {
return(
hourOfDay => '*',
minuteOfHour => '*/2',
);
}
elsif ($interval eq 'every 5 minutes') {
return(
hourOfDay => '*',
minuteOfHour => '*/5',
);
}
elsif ($interval eq 'every 10 minutes') {
return(
hourOfDay => '*',
minuteOfHour => '*/10',
);
}
elsif ($interval eq 'every 15 minutes') {
return(
hourOfDay => '*',
minuteOfHour => '*/15',
);
}
elsif ($interval eq 'every 20 minutes') {
return(
hourOfDay => '*',
minuteOfHour => '*/20',
);
}
elsif ($interval eq 'every 30 minutes') {
return(
hourOfDay => '*',
minuteOfHour => '*/30',
);
}
elsif ($interval eq 'every hour') {
return(
hourOfDay => '*',
minuteOfHour => '0',
);
}
elsif ($interval eq 'every other hour') {
return(
hourOfDay => '*/2',
minuteOfHour => '0',
);
}
elsif ($interval eq 'once per day') {
return(
hourOfDay => '7',
minuteOfHour => '0',
);
}
return(
minuteOfHour => '*/10',
);
}
#-------------------------------------------------------------------
=head2 getHelpers ( )
Add the collaboration-specific asset helpers
@ -1282,6 +1363,35 @@ sub getViewTemplateVars {
return \%var;
}
#--------------------------------------------------------------------------
=head2 groupIdView ( [newvalue] )
Override the existing accessor to update the subscription group if the value
changes.
=cut
around groupIdView => sub {
my $orig = shift;
my $self = shift;
my ( $newValue ) = @_;
my $oldValue = $self->$orig;
my $return = $self->$orig(@_);
# Update the subscription group so if they can't see the collab, they don't get e-mailed
if ( $newValue && $newValue != $oldValue ) {
my $instance_data = {
workflowId => 'xR-_GRRbjBojgLsFx3dEMA',
className => 'WebGUI::Asset',
methodName => 'newById',
parameters => $self->getId,
};
my $instance = WebGUI::Workflow::Instance->create($self->session, $instance_data);
$instance->start('skipRealtime');
}
return $return;
};
#-------------------------------------------------------------------
=head2 incrementReplies ( lastPostDate, lastPostId )
@ -1586,7 +1696,6 @@ sub unsubscribe {
$group->deleteUsers([$user->userId]);
}
#-------------------------------------------------------------------
=head2 view

View file

@ -320,13 +320,17 @@ override purge => sub {
=head2 view
Render the dashboard.
Render the dashboard. Of all the positions for content, position1 is reserved for hidden content
or content to be placed. Deleting content causes it to go into position1.
=cut
sub view {
my $self = shift;
my $self = shift;
my $session = $self->session;
my %vars = %{$self->get()};
$vars{canEdit} = $self->canEdit;
$vars{fullUrl} = $self->getUrl;
$self->session->style->setScript( $self->session->url->extras('yui/build/utilities/utilities.js'));
@ -356,60 +360,85 @@ sub view {
my @found;
my $newStuff;
my $showPerformance = $self->session->log->performanceLogger();
my $user = $self->session->user;
foreach my $position (@positions) {
my @assets = split(",",$position);
foreach my $asset (@assets) {
foreach my $child (@{$children}) {
CHILD: foreach my $child (@{$children}) {
if ($asset eq $child->getId) {
unless ($asset ~~ @hidden || !$child->canView) {
$self->session->style->setRawHeadTags($child->getExtraHeadTags);
$child->{_properties}{title} = $child->getTitle;
$child->{_properties}{title} = $child->getShortcut->getTitle if (ref $child eq 'WebGUI::Asset::Shortcut');
if ($i == 1 || $i > $numPositions) {
push(@{$vars{"position1_loop"}},{
id=>$child->getId,
content=>'', #so things in the New Content bar don't display.
dashletTitle=>$child->{_properties}{title},
shortcutUrl=>$child->getUrl,
canPersonalize=>$self->canPersonalize,
showReloadIcon=>$child->{_properties}{showReloadIcon},
canEditUserPrefs=>(($self->session->user->isRegistered) && (ref $child eq 'WebGUI::Asset::Shortcut') && (scalar($child->getPrefFieldsToShow) > 0))
});
$newStuff .= 'available_dashlets["'.$child->getId.'"]=\''.$child->getUrl.'\';';
} else {
$child->prepareView;
push(@{$vars{"position".$i."_loop"}},{
id=>$child->getId,
content=>$child->view,
dashletTitle=>$child->{_properties}{title},
shortcutUrl=>$child->getUrl,
canPersonalize=>$self->canPersonalize,
showReloadIcon=>$child->{_properties}{showReloadIcon},
canEditUserPrefs=>(($self->session->user->isRegistered) && (ref $child eq 'WebGUI::Asset::Shortcut') && (scalar($child->getPrefFieldsToShow) > 0))
});
$newStuff .= 'available_dashlets["'.$child->getId.'"]=\''.$child->getUrl.'\';';
}
}
push(@found, $child->getId);
}
##Filter based on visibility
next CHILD unless $child->canView;
next CHILD if isIn($asset, @hidden);
##Detect child types
my $is_shortcut = $child->isa('WebGUI::Asset::Shortcut');
my $is_dashlet = $child->can('getOverrideFormDefinition');
$self->session->style->setRawHeadTags($child->getExtraHeadTags);
##Override the title for shortcuts
if ($is_shortcut) {
$child->{_properties}{title} = $child->getShortcut->getTitle;
}
##Fetch dashlet options from the database
my $options = $session->db->quickHashRef('select * from Dashboard_dashlets where dashboardAssetId=? and dashletAssetId=?', [$self->getId, $child->getId]);
if (!($i == 1 || $i > $numPositions)) {
$child->prepareView;
}
my $spot = $i > $numPositions ? 1 : $i;
my $canMove = $self->canPersonalize && !$options->{isStatic};
my $editFormUrl = ($is_shortcut && $child->getPrefsFieldToShow) ? $child->getUrl('func=getUserPrefsForm')
: ($is_dashlet && $child->getOverrideFormDefinition) ? $self->getUrl('func=customizeDashlet;dashletAssetId='.$child->getId)
: ''
;
my $canEditUserPrefs = $user->isRegistered && $editFormUrl;
push(@{$vars{"position".$spot."_loop"}},{
id => $child->getId,
content => $child->view,
dashletTitle => $child->get('title'),
shortcutUrl => ($is_shortcut ? $child->getUrl : ''),
editFormUrl => $editFormUrl,
dashletUrl => ($is_dashlet ? $child->getUrl : ''),
canDelete => $self->canPersonalize && !$options->{isRequired},
canMove => $canMove,
canPersonalize => $self->canPersonalize,
showReloadIcon => $is_shortcut && $child->get('showReloadIcon'),
canEditUserPrefs => $canEditUserPrefs,
});
if ($canMove) {
$newStuff .= 'available_dashlets["'.$child->getId.'"]=\''.$child->getUrl.'\';';
}
}
}
}
$i++;
}
# deal with unplaced children
# deal with unplaced children, they go into position 1
foreach my $child (@{$children}) {
unless ($child->getId ~~ @found || $child->getId ~~ @hidden) {
if ($child->canView) {
$child->{_properties}{title} = $child->getShortcut->title if (ref $child eq 'WebGUI::Asset::Shortcut');
my $is_shortcut = $child->isa('WebGUI::Asset::Shortcut');
my $is_dashlet = $child->can('getOverrideFormDefinition');
my $title = $child->{_properties}{title} = $is_shortcut ? $child->getShortcut->getTitle : $child->getTitle;
my $options = $session->db->quickHashRef('select * from Dashboard_dashlets where dashboardAssetId=? and dashletAssetId=?', [$self->getId, $child->getId]);
my $canMove = $self->canPersonalize && !$options->{isStatic};
my $editFormUrl = ($is_shortcut && $child->getPrefFieldsToShow) ? $child->getUrl('func=getUserPrefsForm')
: ($is_dashlet && $child->getOverrideFormDefinition) ? $self->getUrl('func=customizeDashlet;dashletAssetId='.$child->getId)
: ''
;
my $canEditUserPrefs = $user->isRegistered && $editFormUrl;
push(@{$vars{"position1_loop"}},{
id=>$child->getId,
content=>'',
dashletTitle=>$child->getTitle,
shortcutUrl=>$child->getUrl,
showReloadIcon=>$child->{_properties}{showReloadIcon},
canPersonalize=>$self->canPersonalize,
canEditUserPrefs=>(($self->session->user->isRegistered) && (ref $child eq 'WebGUI::Asset::Shortcut') && (scalar($child->getPrefFieldsToShow) > 0))
id => $child->getId,
content => '',
dashletTitle => $title,
shortcutUrl => ($is_shortcut ? $child->getUrl : ''),
editFormUrl => $editFormUrl,
dashletUrl => ($is_dashlet ? $child->getUrl : ''),
canDelete => $self->canPersonalize && !$options->{isRequired},
canMove => $canMove,
canPersonalize => $self->canPersonalize,
showReloadIcon => $is_shortcut && $child->{_properties}{showReloadIcon},
canEditUserPrefs => $canEditUserPrefs,
});
$newStuff .= 'available_dashlets["'.$child->getId.'"]=\''.$child->getUrl.'\';';
}
@ -418,9 +447,9 @@ sub view {
$vars{showAdmin} = ($self->session->isAdminOn && $self->canEdit);
$vars{"dragger.init"} = '
<script type="text/javascript">
dragable_init("'.$self->getUrl.'");
var available_dashlets= new Array();
'.$newStuff.'
dragable_init("'.$self->getUrl.'");
</script>
';
return $self->processTemplate(\%vars, $templateId);
@ -428,6 +457,128 @@ sub view {
#-------------------------------------------------------------------
=head2 www_customizeDashlet
Web facing method for saving per dashlet configuration, such as being required, or movable.
=cut
sub www_customizeDashlet {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless ($session->user->isRegistered);
my $dashletAssetId = $session->form->get('dashletAssetId');
my $dashlet = WebGUI::Asset->newById($session, $dashletAssetId);
return $session->privilege->insufficient() unless ($dashlet && $dashlet->canView && $dashlet->can('getOverrideFormDefinition'));
my $i18n = WebGUI::International->new($session, 'Asset_Dashboard');
my $form = $session->form;
my $html_form = WebGUI::HTMLForm->new($session, action => $self->getUrl, method => 'POST', );
$html_form->hidden(name => 'func', value => 'customizeDashletSave', );
$html_form->hidden(name => 'dashletAssetId', value => $dashletAssetId, );
$html_form->readOnly(name => $i18n->get(), value => $dashlet->getTitle, );
my $overrides = $dashlet->fetchUserOverrides($self->getId);
my @dashlet_properties = $dashlet->getOverrideFormDefinition;
foreach my $property (@dashlet_properties) {
my %properties = %{ $property };
$properties{value} = $overrides->{$property->{name}} || $dashlet->get($property->{name});
$html_form->dynamicField(%properties);
}
$html_form->submit();
return $html_form->print;
}
#-------------------------------------------------------------------
=head2 www_customizeDashletSave
Web facing method for saving per dashlet configuration, such as being required, or movable.
=cut
sub www_customizeDashletSave {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless ($session->user->isRegistered);
my $dashletAssetId = $session->form->get('dashletAssetId');
my $dashlet = WebGUI::Asset->newById($session, $dashletAssetId);
return $session->privilege->insufficient() unless ($dashlet && $dashlet->canView && $dashlet->can('getOverrideFormDefinition'));
my $overrides = {};
my @dashlet_properties = $dashlet->getOverrideFormDefinition;
my $form = $session->form;
foreach my $property (@dashlet_properties) {
my $value = $form->process($property->{name}, $property->{fieldType}, $property->{value});
$overrides->{$property->{name}} = $value;
}
$dashlet->storeUserOverrides($self->getId, $overrides);
return $self->www_view;
}
##-------------------------------------------------------------------
=head2 www_editDashlet
Web facing method for saving per dashlet configuration, such as being required, or movable.
=cut
sub www_editDashlet {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless ($self->canEdit);
my $dashletAssetId = $session->form->get('dashletAssetId');
my $dashlet = WebGUI::Asset->newById($session, $dashletAssetId);
return $session->privilege->insufficient() unless ($dashletAssetId);
my $i18n = WebGUI::International->new($session, 'Asset_Dashboard');
my $form = $session->form;
my $html_form = WebGUI::HTMLForm->new($session, action => $self->getUrl, method => 'POST', );
$html_form->hidden(name => 'func', value => 'editDashletSave', );
$html_form->hidden(name => 'dashletAssetId', value => $dashletAssetId, );
$html_form->readOnly(name => $i18n->get(), value => $dashlet->getTitle, );
my $options = $session->db->quickHashRef('select * from Dashboard_dashlets where dashboardAssetId=? and dashletAssetId=?', [$self->getId, $dashletAssetId]);
$html_form->yesNo(
name => 'isStatic',
label => $i18n->get('Is static'),
hoverHelp => $i18n->get('Is static help'),
value => $form->get('isStatic') || $options->{isStatic},
);
$html_form->yesNo(
name => 'isRequired',
label => $i18n->get('Is required'),
hoverHelp => $i18n->get('Is required help'),
value => $form->get('isRequired') || $options->{isRequired},
);
$html_form->submit();
return $html_form->print;
}
#-------------------------------------------------------------------
=head2 www_editDashletSave
Web facing method for saving per dashlet configuration, such as being required, or movable.
=cut
sub www_editDashletSave {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless ($self->canEdit);
my $dashletAssetId = $session->form->get('dashletAssetId');
my $isStatic = $session->form->get('isStatic', 'yesNo');
my $isRequired = $session->form->get('isRequired', 'yesNo');
$session->db->write('DELETE FROM Dashboard_dashlets where dashboardAssetId=? and dashletAssetId=?',[$self->getId, $dashletAssetId, ]);
$session->db->write('INSERT INTO Dashboard_dashlets (dashboardAssetId, dashletAssetId, isStatic, isRequired) VALUES (?,?,?,?)', [$self->getId, $dashletAssetId, $isStatic, $isRequired, ]);
return $self->www_view;
}
#-------------------------------------------------------------------
=head2 www_setContentPositions
Web method for saving the positions of dashlets in the dashboard.

View file

@ -320,12 +320,13 @@ sub _fieldAdminIcons {
my $self = shift;
my $fieldName = shift;
my $i18n = WebGUI::International->new($self->session,"Asset_DataForm");
my $mode = ";mode=" . $self->currentView;
my $output;
$output = $self->session->icon->delete('func=deleteFieldConfirm;fieldName='.$fieldName,$self->url,$i18n->get(19))
$output = $self->session->icon->delete('func=deleteFieldConfirm;fieldName='.$fieldName.$mode,$self->get("url"),$i18n->get(19))
unless $self->getFieldConfig($fieldName)->{isMailField};
$output .= $self->session->icon->edit('func=editField;fieldName='.$fieldName,$self->url)
. $self->session->icon->moveUp('func=moveFieldUp;fieldName='.$fieldName,$self->url)
. $self->session->icon->moveDown('func=moveFieldDown;fieldName='.$fieldName,$self->url);
$output .= $self->session->icon->edit('func=editField;fieldName='.$fieldName.$mode,$self->get("url"))
. $self->session->icon->moveUp('func=moveFieldUp;fieldName='.$fieldName.$mode,$self->get("url"))
. $self->session->icon->moveDown('func=moveFieldDown;fieldName='.$fieldName.$mode,$self->get("url"));
return $output;
}
#-------------------------------------------------------------------
@ -784,6 +785,7 @@ sub getListTemplateVars {
'field.label' => $fieldConfig->{$_}{label},
'field.isMailField' => $fieldConfig->{$_}{isMailField},
'field.type' => $fieldConfig->{$_}{type},
"field.controls" => $self->_fieldAdminIcons($fieldConfig->{$_}{name}),
}
} @{ $self->getFieldOrder };
$var->{field_loop} = \@fieldLoop;

View file

@ -103,7 +103,7 @@ Overridden to check the revision dates of children as well
sub getContentLastModified {
my $self = shift;
my $mtime = $self->revisionDate;
my $mtime = $self->get("lastModified");
my $childIter = $self->getLineageIterator(["children"]);
while ( 1 ) {
my $child;
@ -119,6 +119,60 @@ sub getContentLastModified {
return $mtime;
}
#-------------------------------------------------------------------
=head2 getContentLastModifiedBy
Overridden to check the updated dates of children as well
=cut
sub getContentLastModifiedBy {
my $self = shift;
my $mtime = $self->SUPER::getContentLastModified;
my $userId = $self->get('revisedBy');
my $childIter = $self->getLineageIterator(["children"]);
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
my $child_mtime = $child->getContentLastModified;
if ($child_mtime > $mtime) {
$mtime = $child_mtime;
$userId = $child->get("revisedBy");
}
}
return $userId;
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
Returns the TabForm object that will be used in generating the edit page for this asset.
=cut
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
my $i18n = WebGUI::International->new($self->session,"Asset_Folder");
if ($self->get("assetId") eq "new") {
$tabform->getTab("properties")->whatNext(
-options=>{
view=>$i18n->get(823),
"viewParent"=>$i18n->get(847)
},
-value=>"view"
);
}
return $tabform;
}
#----------------------------------------------------------------------------
=head2 getTemplateVars ( )

View file

@ -363,6 +363,36 @@ override getContentLastModified => sub {
#-------------------------------------------------------------------
=head2 getContentLastModifiedBy
Extend the base class to include the userid of the person that made last modification.
=cut
sub getContentLastModifiedBy {
my $self = shift;
my $mtime = $self->SUPER::getContentLastModified;
my $userId = $self->get('revisedBy');
my $childIter = $self->getLineageIterator(["children"],{excludeClasses=>['WebGUI::Asset::Wobject::Layout']});
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
my $child_mtime = $child->getContentLastModified;
if ($child_mtime > $mtime) {
$mtime = $child_mtime;
$userId = $child->get("revisedBy");
}
}
return $userId;
}
#-------------------------------------------------------------------
=head2 www_view
Extend the base method to handle caching and ad rotation.

View file

@ -371,7 +371,9 @@ sub view {
google.setOnLoadCallback( function() {
var mapId = "%s";
var mapUrl = "%s";
var map = new GMap2( document.getElementById("map_" + mapId) );
var element = document.getElementById("map_" + mapId);
var map = new GMap2( element );
element.mapObject = map;
map.url = mapUrl;
map.assetId = mapId;
map.setCenter(new GLatLng(%s, %s), %s);
@ -379,6 +381,10 @@ sub view {
map.extrasUrl = "%s";
var markermanager = new MarkerManager(map, {trackMarkers: true});
map.markermanager = markermanager;
map.fancyClickHandler = null;
map.panOnClick = true;
map.clickToEdit = false;
ENDHTML
@ -392,9 +398,9 @@ ENDHTML
for my $pointId ( @{$pointIds} ) {
my $point = WebGUI::Asset->newById( $session, $pointId );
next unless $point;
$mapHtml .= sprintf ' points.push(%s);'."\n",
JSON->new->encode($point->getMapInfo),
;
my $buffer = JSON->new->encode( $point->getMapInfo );
$mapHtml .= sprintf ' points.push(%s);'."\n", $buffer;
push @{$var->{ mapPoints }}, $point->getTemplateVars;
}
@ -416,11 +422,11 @@ ENDHTML
}
# Script to control addPoint and setPoint buttons
$mapHtml .= <<'ENDHTML';
$mapHtml .= sprintf <<'ENDHTML', $self->getUrl;
if ( document.getElementById( "setCenter_" + mapId ) ) {
var button = document.getElementById( "setCenter_" + mapId );
GEvent.addDomListener( button, "click", function () {
WebGUI.Map.setCenter( map );
WebGUI.Map.setCenter( map, '%s' );
} );
}
if ( document.getElementById( "addPoint_" + mapId ) ) {

View file

@ -175,30 +175,31 @@ sub view {
);
my @results = ();
foreach my $data (@{$p->getPageData}) {
next unless (
ENTRY: foreach my $data (@{$p->getPageData}) {
next ENTRY unless (
$user->userId eq $data->{ownerUserId}
|| $user->isInGroup($data->{groupIdView})
|| $user->isInGroup($data->{groupIdEdit})
);
my $asset = WebGUI::Asset->newById($session, $data->{assetId});
if (defined $asset) {
my $properties = $asset->get;
if ($self->useContainers) {
$properties->{url} = $asset->isa('WebGUI::Asset::Post::Thread') ? $asset->getCSLinkUrl()
: $asset->getContainer->url;
}
#Add highlighting
$properties->{'title' } = $hl->highlight($properties->{title} || '');
$properties->{'title_nohighlight' } = $properties->{title};
my $synopsis = $data->{'synopsis'} || '';
WebGUI::Macro::process($self->session, \$synopsis);
$properties->{'synopsis' } = $hl->highlight($synopsis);
$properties->{'synopsis_nohighlight'} = $synopsis;
push(@results, $properties);
$var{results_found} = 1;
}
next ENTRY unless defined $asset;
my $properties = $asset->get;
##Overlay the asset properties with the original data to handle sub-entries for assets
$properties = { %{ $properties }, %{ $data} };
if ( $self->get("useContainers") && $asset->getContainer->canView ) {
$properties->{url} = $asset->isa('WebGUI::Asset::Post::Thread') ? $asset->getCSLinkUrl()
: $asset->getContainer->get("url");
}
#Add highlighting
$properties->{'title' } = $hl->highlight($properties->{title} || '');
$properties->{'title_nohighlight' } = $properties->{title};
my $synopsis = $properties->{'synopsis'} || '';
WebGUI::Macro::process($self->session, \$synopsis);
$properties->{'synopsis' } = $hl->highlight($synopsis);
$properties->{'synopsis_nohighlight'} = $synopsis;
push(@results, $properties);
$var{results_found} = 1;
}
$var{result_set} = \@results;

View file

@ -20,6 +20,7 @@ use Number::Format ();
use Moose;
use WebGUI::Definition::Asset;
extends 'WebGUI::Asset::Wobject';
with 'WebGUI::Role::Asset::Dashlet';
define tableName => 'StockData';
define icon => 'stockData.gif';
define assetName => ["assetName", 'Asset_StockData'];
@ -45,6 +46,7 @@ property defaultStocks => (
tab => 'properties',
label => ["default_stock_label", 'Asset_StockData'],
hoverHelp => ["default_stock_label_description", 'Asset_StockData'],
dashletOverridable => 1,
);
property source => (
fieldType => "selectList",
@ -208,9 +210,12 @@ sub _convertToEpoch {
if($time =~ m/pm/i) {
$hour += 12;
}
$hour ||= 0;
$hour = $self->_appendZero($hour);
$minute ||= 0;
$minute = $self->_appendZero($minute);
return $self->session->datetime->humanToEpoch("$year-$month-$day $hour:$minute:00");
my $epoch = eval {$self->session->datetime->humanToEpoch("$year-$month-$day $hour:$minute:00")};
return $epoch;
}
#-------------------------------------------------------------------
@ -226,21 +231,45 @@ List of stock symbols to find passed in as an array reference. Stock symbols sh
=cut
sub _getStocks {
my $self = shift;
my $stocks = $_[0];
my $self = shift;
my $stocks = $_[0];
my $session = $self->session;
# Create a new Finance::Quote object
my $q = Finance::Quote->new;
# Disable failover if specified
unless ($self->failover) {
$q->failover(0);
}
# Create a new Finance::Quote object
my $q = Finance::Quote->new;
# Disable failover if specified
unless ($self->failover) {
$q->failover(0);
}
# Hardcoded timeout for now.
$q->timeout(15);
# Hardcoded timeout for now.
$q->timeout(15);
my $source = $self->source;
my %stocks = ();
my @stocks_to_fetch = ();
STOCK: foreach my $stock (@{$stocks}) {
$stock = uc $stock;
my $value = $session->cache->get( join "", $self->getId, $source, $stock );
if ($value) {
%stocks = (%stocks, %{ $value });
}
else {
push @stocks_to_fetch, $stock;
}
}
# Fetch the stock information and return the results
return $q->fetch($self->source,@{$stocks});
# Fetch the information for uncached stocks, cache them individually, and build the composite data.
my %new_stocks = $q->fetch($source, @stocks_to_fetch);
foreach my $stock (@stocks_to_fetch) {
$stock = uc $stock;
my @stock_keys = grep { /$stock\b/ } keys %new_stocks;
my %slice;
@slice{ @stock_keys } = @new_stocks{ @stock_keys };
$slice{$stock,'last_fetch'} = time();
$session->cache->set( join( "", $self->getId, $source, $stock ), \%slice, $self->get('cacheTimeout') );
%stocks = (%stocks, %slice);
}
return \%stocks;
}
#-------------------------------------------------------------------
@ -330,7 +359,8 @@ sub view {
$var->{'stock.display.url'} = $self->getUrl("func=displayStock;symbol=");
#Build list of stocks as an array
my $defaults = $self->defaultStocks;
my $overrides = $self->fetchUserOverrides($self->getParent->getId);
my $defaults = $overrides->{defaultStocks} || $self->defaultStocks;
#replace any windows newlines
$defaults =~ s/\r//;
my @array = split("\n",$defaults);

View file

@ -171,38 +171,48 @@ sub viewTemplateVariables {
rowsPerPage => $numberOfStories,
});
my $storyIds = $p->getPageData();
$var->{story_loop} = [];
my $icon = $session->icon;
my $userUiLevel = $session->user->get("uiLevel");
my $uiLevels = $session->config->get('assetToolbarUiLevel');
my $i18n = WebGUI::International->new($session);
my $url = $session->url;
##Only build objects for the assets that we need
STORY: foreach my $storyId (@{ $storyIds }) {
my $story = WebGUI::Asset->newById($session, $storyId->{assetId}, $storyId->{revisionDate});
next STORY unless $story;
my $storyVars = {
url => ( $exporting
? $story->getUrl
: $session->url->append($self->getUrl, 'func=viewStory;assetId='.$storyId->{assetId}) ),
title => $story->getTitle,
creationDate => $story->creationDate,
};
if ($story->canEdit && $userUiLevel >= $uiLevels->{delete} && !$exporting) {
$storyVars->{deleteIcon} = $icon->delete('func=delete', $story->url, $i18n->get(43));
$var->{story_loop} = [
map {
my $v = $_->viewTemplateVariables;
if ($exporting) {
$v->{url} = $_->getUrl;
}
else {
my $params = "func=viewStory;assetId=$v->{assetId}";
my $rawUrl = $v->{url};
$v->{url} = $url->append($self->getUrl, $params);
if ($v->{canEdit}) {
if ($userUiLevel >= $uiLevels->{delete}) {
$v->{deleteIcon} = $icon->delete('func=delete', $rawUrl, $i18n->get(43));
}
if ($userUiLevel >= $uiLevels->{edit}) {
$v->{editIcon} = $icon->edit('func=edit', $rawUrl);
}
}
}
$v;
}
if ($story->canEdit && $userUiLevel >= $uiLevels->{edit} && !$exporting) {
$storyVars->{editIcon} = $icon->edit('func=edit', $story->url);
grep { $_ }
map {
WebGUI::Asset->newById( $session, $_->{assetId} )
}
push @{$var->{story_loop}}, $storyVars;
}
@{ $storyIds }
];
if (@{ $storyIds }) {
my $topStoryData = $storyIds->[0];
my $topStoryVars = shift @{ $var->{story_loop} };
##Note, this could have saved from the loop above, but this looks more clean and encapsulated to me.
my $topStory = WebGUI::Asset->newById($session, $topStoryData->{assetId}, $topStoryData->{revisionDate});
$var->{topStory} = $topStoryVars;
$var->{topStoryTitle} = $topStory->getTitle;
$var->{topStorySubtitle} = $topStory->subtitle;
$var->{topStoryUrl} = $session->url->append($self->getUrl, 'func=viewStory;assetId='.$topStoryData->{assetId}),

View file

@ -2336,12 +2336,12 @@ sub www_viewStatisticalOverview {
}
}
else{
my $responses = $db->read('select value,answerComment from Survey_tempReport'
my $responses = $db->read('select answerValue,answerComment from Survey_tempReport'
. ' where sectionNumber=? and questionNumber=?',
[$sectionIndex,$questionIndex]);
while (my $response = $responses->hashRef) {
push @answerloop,{
'answer_value' =>$response->{value},
'answer_value' =>$response->{answerValue},
'answer_comment' =>$response->{answerComment}
};
}

View file

@ -97,6 +97,7 @@ has '+uiLevel' => (
with 'WebGUI::Role::Asset::RssFeed';
use WebGUI::Macro;
use XML::FeedPP;
use XML::FeedPP::MediaRSS;
=head1 NAME
@ -252,6 +253,7 @@ A reference to an XML::FeedPP object.
sub getTemplateVariables {
my ($self, $feed) = @_;
my $media = XML::FeedPP::MediaRSS->new($feed);
my @items = $feed->get_item;
my %var;
$var{channel_title} = WebGUI::HTML::filter(scalar $feed->title, 'javascript');
@ -268,6 +270,7 @@ sub getTemplateVariables {
$var{channel_image_height} = WebGUI::HTML::filter($image[5], 'javascript');
foreach my $object (@items) {
my %item;
$item{media} = [ map { { %$_ } } $media->for_item($object) ];
$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');

View file

@ -260,6 +260,24 @@ sub badOtherThing {
#-------------------------------------------------------------------
=head2 deleteThingIndex ( $thingId )
Remove the entry about this Thing from the database, and for any rows for it that are indexed as well.
=head3 $thingId
The GUID of the Thing, used to pick out this record in the database.
=cut
sub deleteThingIndex {
my $self = shift;
my $thingId = shift;
$self->session->db->write(q|delete from assetIndex where assetId=? and subId like CONCAT(?,'%')|,[$self->getId, $thingId]);
}
#-------------------------------------------------------------------
=head2 duplicate ( )
Duplicates a Thingy, including the definitions of the Things in this Thingy and their fields.
@ -346,6 +364,8 @@ sub duplicateThing {
$self->addField($field,0);
}
$thingProperties->{thingId} = $newThingId;
$self->indexThing($thingProperties);
return $newThingId;
}
@ -397,6 +417,7 @@ sub deleteField {
$db->write("ALTER TABLE ".$db->quote_identifier("Thingy_".$thingId)." DROP "
.$db->quote_identifier("field_".$fieldId));
}
$self->reindexThings;
$log->info("Deleted field: $fieldId in thing: $thingId.");
return undef;
}
@ -433,7 +454,7 @@ sub copyThingData {
my @storage_field_ids = ();
##Check to see if any of them are File or Image
foreach my $field (@{ $fields }) {
if ($field->{fieldType} eq 'File' or $field->{fieldType} eq 'Image') {
if ($self->field_isa($field->{fieldType}, 'WebGUI::Form::File')) {
push @storage_field_ids, $field->{fieldId};
}
}
@ -447,6 +468,7 @@ sub copyThingData {
}
##Update the copy
$self->setCollateral("Thingy_".$thingId, "thingDataId", $origCollateral, 0, 0);
$self->indexThingData($thingId, $origCollateral);
return undef;
}
@ -491,7 +513,7 @@ sub deleteThingData {
my @storage_field_ids = ();
##Check to see if any of them are File or Image
foreach my $field (@{ $fields }) {
if ($field->{fieldType} eq 'File' or $field->{fieldType} eq 'Image') {
if ($self->field_isa($field->{fieldType}, 'WebGUI::Form::File')) {
push @storage_field_ids, $field->{fieldId};
}
}
@ -501,11 +523,30 @@ sub deleteThingData {
$storage->delete;
}
$self->deleteThingDataIndex($thingDataId);
return undef;
}
#-------------------------------------------------------------------
=head2 deleteThingDataIndex ( $thingDataId )
Remove the entry about this Thing from the database, and for any rows for it that are indexed as well.
=head3 $thingDataId
The GUID of the thingData to remove, used to pick out this record in the database.
=cut
sub deleteThingDataIndex {
my $self = shift;
my $thingDataId = shift;
$self->session->db->write(q|delete from assetIndex where assetId=? and subId like CONCAT('%-',?)|,[$self->getId, $thingDataId]);
}
#-------------------------------------------------------------------
=head2 deleteThing ( thingId )
Deletes a Thing and its fields from Collateral and drops the things table.
@ -528,6 +569,7 @@ sub deleteThing {
$session->db->write("drop table if exists ".$session->db->quote_identifier("Thingy_".$thingId));
$log->info("Deleted thing: $thingId.");
$self->deleteThingIndex($thingId);
return undef;
}
@ -535,7 +577,9 @@ sub deleteThing {
=head2 editThingDataSave ( )
Saves a row of thing data and triggers the appropriate workflow triggers.
Saves a row of thing data and triggers the appropriate workflow triggers. Returns the id of the row created in
the database, and an array reference of errors from required fields and other sources. In there are errors, no data
is saved in the database, and the id returned in the empty string.
=head3 thingId
@ -579,8 +623,7 @@ sub editThingDataSave {
lastUpDated=>time(),
);
$fields = $session->db->read('select * from Thingy_fields where assetId = ? and thingId = ? order by sequenceNumber',
[$self->getId,$thingId]);
$fields = $self->getFields($thingId);
while (my $field = $fields->hashRef) {
my $fieldName = 'field_'.$field->{fieldId};
my $fieldValue;
@ -589,7 +632,7 @@ sub editThingDataSave {
$fieldType = "" if ($fieldType =~ m/^otherThing/x);
# Modify the defaultValue for certain field types. For most types we want to use
# the default in the database, for these we want the last known value for this thingData
if ( $fieldType eq "File" || $fieldType eq "Image" ) {
if ($self->field_isa($fieldType, 'WebGUI::Form::File')) {
$field->{ defaultValue } = $thingData{ "field_" . $field->{ fieldId } };
}
$fieldValue = $thingData->{$fieldName} || $session->form->process($fieldName,$fieldType,$field->{defaultValue},$field);
@ -598,7 +641,6 @@ sub editThingDataSave {
push (@errors,{
"error_message"=>$field->{label}." ".$i18n->get('is required error').".",
});
#$hadErrors = 1;
}
if ($field->{status} eq "hidden") {
$fieldValue = $field->{defaultValue};
@ -611,7 +653,11 @@ sub editThingDataSave {
$thingData{$fieldName} = $fieldValue;
}
if (@errors) {
return ('', \@errors);
}
$newThingDataId = $self->setCollateral("Thingy_".$thingId,"thingDataId",\%thingData,0,0);
$self->indexThingData($thingId, \%thingData);
# trigger workflow
if($thingDataId eq "new"){
@ -653,6 +699,33 @@ override exportAssetData => sub {
#-------------------------------------------------------------------
=head2 field_isa ( $fieldType, $isa )
Builds a form field and does an isa check on it.
=head2 $fieldType
This is the type of a field to build. It will have 'WebGUI::Form' prepended to it to form
a complete classname.
=head2 $isa
This is the class name to check against.
=cut
sub field_isa {
my $self = shift;
my $session = $self->session;
my $fieldType = shift;
my $isa = shift;
$fieldType = ucfirst $fieldType;
my $control = eval { WebGUI::Pluggable::instanciate("WebGUI::Form::".$fieldType, "new", [ $session, () ]) };
return ($control && $control->isa($isa));
}
#-------------------------------------------------------------------
=head2 _getDbDataType ( fieldType )
returns the database data type for a field based on the fieldType.
@ -905,6 +978,23 @@ sub getEditFieldForm {
#-------------------------------------------------------------------
=head2 getFields ( $thingId )
Returns a result set with all the fields in a thing in this Thingy.
=head3 $thingId
The GUID for a thing
=cut
sub getFields {
my ($self, $thingId) = @_;
return $self->session->db->read("select * from Thingy_fields where assetId=? and thingId=? order by sequenceNumber",[$self->getId, $thingId]);
}
#-------------------------------------------------------------------
=head2 getFieldValue ( value, field )
Processes the field value for date(Time) fields and Other Thing fields.
@ -1153,6 +1243,29 @@ sub getThing {
#-------------------------------------------------------------------
=head2 getThingUrl ( thingData )
Returns a the URL to view a Thing in this Thingy
=head3 thingData
A hashref of properties for the Thing, as returned by getThing. This is needed to extract the defaultView,
to get the right func, and the thingId.
=cut
sub getThingUrl {
my ($self, $thing) = @_;
if ($thing->{defaultView} eq "addThing") {
return 'func=editThingData;thingId='.$thing->{thingId}.';thingDataId=new';
}
else{
return 'func=search;thingId='.$thing->{thingId};
}
}
#-------------------------------------------------------------------
=head2 getViewThingVars ( )
Returns the field values of a thing instance and the title for its view screen in a tmpl var hashref.
@ -1171,8 +1284,7 @@ sub getViewThingVars {
." where thingDataId = ?",[$thingDataId]);
if (%thingData) {
my $fields = $db->read('select * from Thingy_fields where assetId = ? and thingId = ? order by sequenceNumber',
[$self->getId,$thingId]);
my $fields = $self->getFields($thingId);
while (my %field = $fields->hash) {
next unless ($field{display} eq '1');
my $hidden = ($field{status} eq "hidden" && !$self->session->isAdminOn);
@ -1207,6 +1319,7 @@ sub getViewThingVars {
);
push(@viewScreenTitleFields,$value) if ($field{viewScreenTitle});
push(@field_loop, { map {("field_".$_ => $fieldProperties{$_})} keys(%fieldProperties) });
$var->{ $field{label} } = $value;
}
$var->{viewScreenTitle} = join(" ",@viewScreenTitleFields);
$var->{field_loop} = \@field_loop;
@ -1314,7 +1427,7 @@ sub importAssetCollateralData {
push(@importThings,$thing->{thingId});
my ($thingIdExists) = $session->db->quickArray("select thingId from Thingy_things where thingId = ?",
[$thing->{thingId}]);
if ($assetExists && $thingIdExists){
if ($thingIdExists){
# update existing thing
$log->info("Updating Thing, label: ".$thing->{label}.", id: ".$thing->{thingId});
$self->setCollateral("Thingy_things","thingId",$thing,0,0);
@ -1323,6 +1436,7 @@ sub importAssetCollateralData {
# add new thing
$self->addThing($thing,1);
}
$self->indexThing($thing);
}
# delete deleted things
my $thingsInDatabase = $self->getThings;
@ -1338,7 +1452,7 @@ sub importAssetCollateralData {
push(@importFields,$field->{fieldId});
my $dbDataType = $self->_getDbDataType($field->{fieldType});
my ($fieldIdExists) = $session->db->quickArray("select fieldId from Thingy_fields where fieldId = ? and thingId = ? ",[$field->{fieldId},$field->{thingId}]);
if ($assetExists && $fieldIdExists){
if ($fieldIdExists){
# update existing field
$log->info("Updating Field, label: ".$field->{label}.", id: ".$field->{fieldId}.",seq :"
.$field->{sequenceNumber});
@ -1364,6 +1478,126 @@ sub importAssetCollateralData {
}
#-------------------------------------------------------------------
=head2 indexContent ( )
Extend the base class to handle reindexing every Thing, and every row in every Thing. We have
to do a complete rework because the URL may have changed, and that affects everything that has
been inserted into the AssetIndex.
=cut
sub indexContent {
my ($self) = @_;
my $session = $self->session;
$self->SUPER::indexContent();
$self->reindexThings;
}
#-------------------------------------------------------------------
=head2 indexThing ( $thing )
Add an entry about this Thing into the AssetIndex so it can be found for searches.
=head3 $thing
A hashref of Thing properties, as returned by getThing
=cut
sub indexThing {
my ($self, $thing) = @_;
return unless $thing;
my $index = WebGUI::Search::Index->new($self);
$index->addRecord(
url => $self->getUrl($self->getThingUrl($thing)),
groupIdView => $thing->{groupIdView},
title => $thing->{label},
subId => $thing->{thingId},
keywords => join(' ', @{$thing}{qw/label editScreenTitle editInstructions searchScreenTitle searchDescription/}),
);
##Easy update of all thingData fields for this thing. This is in lieu of deleting all records
##And rebuilding them all.
$self->session->db->write(q|update assetIndex set title=?, groupIdView=? where assetId=? and subId like CONCAT(?, '%')|, [$thing->{label}, $thing->{groupIdView}, $self->getId, $thing->{thingId}]);
}
#-------------------------------------------------------------------
=head2 indexThingData ( $thingId, $thingData )
Add an entry about a row from this Thing into the AssetIndex so it can be found for searches.
=head3 $thingId
The GUID for a Thing.
=head3 $thingData
A hashref of ThingData properties
=cut
sub indexThingData {
my ($self, $thingId, $thingData) = @_;
my $session = $self->session;
return unless $thingId;
my $thing = $self->getThing($thingId);
my $index = WebGUI::Search::Index->new($self);
my $keywords;
##Get the Thingy's fields
my $fields = $session->db->read('select * from Thingy_fields where assetId = ? and thingId = ?',
[$self->getId, $thingId]);
##Walk the fields
my @viewScreenTitleFields = ();
FIELD: while (my %field = $fields->hash) {
my $fieldName = 'field_'.$field{fieldId};
$field{value} = $thingData->{$fieldName} || $field{defaultValue};
my $subkeywords = '';
my $value = '';
if ($self->field_isa($field{fieldType}, 'WebGUI::Form::File')) {
my $storage = WebGUI::Storage->get($session, $field{value});
if ($storage) {
foreach my $file (@{$storage->getFiles()}) {
$subkeywords = $index->getKeywordsForFile($storage->getPath($file));
}
}
}
else {
$value = $self->getFieldValue($field{value}, \%field);
##If it's a file type, then get keywords from the file.
##Accumulate keywords
$subkeywords = $value;
}
if ($value && $field{viewScreenTitle}) {
push @viewScreenTitleFields, $value;
}
##We don't index date fields, since they're unix epochs and they'd be different for everyone based on timezone.
next FIELD if $field{fieldType} eq 'date'
|| $field{fieldType} eq 'datetime'
|| $field{fieldType} eq 'time';
##Don't show what shouldn't be shown
next FIELD unless $field{displayInSearch};
$keywords = join ' ', $keywords, $subkeywords;
}
return unless $keywords; ##No sense indexing nothing;
my $title = join('', @viewScreenTitleFields)
|| $thing->{label}
|| $self->getTitle;
$index->addRecord(
assetId => $self->getId,
url => $self->getUrl('func=viewThingData;thingId='. $thing->{thingId} . ';thingDataId='. $thingData->{thingDataId}),
groupIdView => $thing->{groupIdView},
title => $title,
subId => $thing->{thingId} . '-' . $thingData->{thingDataId},
keywords => $keywords,
);
}
#-------------------------------------------------------------------
=head2 prepareView ( )
@ -1412,6 +1646,30 @@ override purge => sub {
return super();
};
#-------------------------------------------------------------------
=head2 reindexThings ( )
Reindex every Thing, and every row in every Thing.
=cut
sub reindexThings {
my ($self) = @_;
my $session = $self->session;
my $things = $self->getThings;
THING: while (my $thing = $things->hashRef) {
$self->deleteThingIndex($thing->{thingId});
$self->indexThing($thing);
my $sth = $session->db->read('select * from '. $session->db->dbh->quote_identifier('Thingy_'.$thing->{thingId}));
while (my $thingData = $sth->hashRef) {
$self->indexThingData($thing->{thingId}, $thingData);
}
$sth->finish;
}
}
#-------------------------------------------------------------------
=head2 triggerWorkflow ( workflowId, thingId, thingDataId )
@ -1734,7 +1992,7 @@ sub www_editThing {
exportMetaData=>undef,
maxEntriesPerUser=>undef,
);
$thingId = $self->addThing(\%properties,0);
$thingId = "new";
}
else{
%properties = %{$self->getThing($thingId)};
@ -1834,15 +2092,15 @@ sub www_editThing {
." <td class='formDescription'>".$i18n->get('sort by label')."</td>\n"
."</tr>\n";
$fields = $self->session->db->read('select * from Thingy_fields where assetId = '.$self->session->db->quote($self->getId).' and thingId = '.$self->session->db->quote($thingId).' order by sequenceNumber');
$fields = $self->getFields($thingId);
while (my $field = $fields->hashRef) {
my $formElement;
if ($field->{fieldType} eq "File"){
$formElement = "<input type='file' name='file'>";
}
if ($field->{fieldType} eq "Image"){
if ($self->field_isa($field->{fieldType}, 'WebGUI::Form::Image')) {
$formElement = "<input type='file' name='image'>";
}
elsif ($self->field_isa($field->{fieldType}, 'WebGUI::Form::File')) {
$formElement = "<input type='file' name='file'>";
}
else{
$formElement = $self->getFormElement($field);
}
@ -1857,11 +2115,12 @@ sub www_editThing {
."\n<table>\n<tr>\n"
." <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='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'>"
." <td style='width:120px;' valign='top'> <input onClick=\"editListItem('"
.$self->getUrl('func=editField;fieldId='.$field->{fieldId}.';thingId='.$thingId)
."','".$field->{fieldId}."')\" value='".$i18n->get('Edit','Icon')."' type='button'>"
." <input onClick=\"editListItem('"
.$self->getUrl('func=editField;copy=1;fieldId='.$field->{fieldId}.';thingId='.$thingId)
."','".$field->{fieldId}."','copy')\" value='".$i18n->get('Copy','Icon')."' 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";
@ -2138,50 +2397,47 @@ database immediately.
=cut
sub www_editThingSave {
my $self = shift;
return $self->session->privilege->insufficient() unless $self->canEdit;
my $form = $self->session->form;
my ($thingId, $fields);
$thingId = $self->session->form->process("thingId");
my $form = $self->session->form;
my $thingId = $self->session->form->process("thingId");
my $fields = $self->getFields($thingId);
$fields = $self->session->db->read('select * from Thingy_fields where assetId = '.$self->session->db->quote($self->getId).' and thingId = '.$self->session->db->quote($thingId).' order by sequenceNumber');
$self->setCollateral("Thingy_things","thingId",{
thingId=>$thingId,
label=>$form->process("label"),
editScreenTitle=>$form->process("editScreenTitle"),
editInstructions=>$form->process("editInstructions"),
groupIdAdd=>$form->process("groupIdAdd"),
groupIdEdit=>$form->process("groupIdEdit"),
saveButtonLabel=>$form->process("saveButtonLabel"),
afterSave=>$form->process("afterSave"),
editTemplateId=>$form->process("editTemplateId") || 1,
onAddWorkflowId=>$form->process("onAddWorkflowId"),
onEditWorkflowId=>$form->process("onEditWorkflowId"),
onDeleteWorkflowId=>$form->process("onDeleteWorkflowId"),
groupIdView=>$form->process("groupIdView"),
viewTemplateId=>$form->process("viewTemplateId") || 1,
defaultView=>$form->process("defaultView"),
searchScreenTitle=>$form->process("searchScreenTitle"),
searchDescription=>$form->process("searchDescription"),
groupIdSearch=>$form->process("groupIdSearch"),
groupIdImport=>$form->process("groupIdImport"),
groupIdExport=>$form->process("groupIdExport"),
searchTemplateId=>$form->process("searchTemplateId") || 1,
thingsPerPage=>$form->process("thingsPerPage") || 25,
sortBy=>$form->process("sortBy") || '',
exportMetaData=>$form->process("exportMetaData") || '',
maxEntriesPerUser=>$form->process("maxEntriesPerUser") || '',
},0,1);
my $thing = {
thingId => $thingId,
label => $form->process("label"),
editScreenTitle => $form->process("editScreenTitle"),
editInstructions => $form->process("editInstructions"),
groupIdAdd => $form->process("groupIdAdd"),
groupIdEdit => $form->process("groupIdEdit"),
saveButtonLabel => $form->process("saveButtonLabel"),
afterSave => $form->process("afterSave"),
editTemplateId => $form->process("editTemplateId") || 1,
onAddWorkflowId => $form->process("onAddWorkflowId"),
onEditWorkflowId => $form->process("onEditWorkflowId"),
onDeleteWorkflowId => $form->process("onDeleteWorkflowId"),
groupIdView => $form->process("groupIdView"),
viewTemplateId => $form->process("viewTemplateId") || 1,
defaultView => $form->process("defaultView"),
searchScreenTitle => $form->process("searchScreenTitle"),
searchDescription => $form->process("searchDescription"),
groupIdSearch => $form->process("groupIdSearch"),
groupIdImport => $form->process("groupIdImport"),
groupIdExport => $form->process("groupIdExport"),
searchTemplateId => $form->process("searchTemplateId") || 1,
thingsPerPage => $form->process("thingsPerPage") || 25,
sortBy => $form->process("sortBy") || '',
exportMetaData => $form->process("exportMetaData") || '',
maxEntriesPerUser => $form->process("maxEntriesPerUser") || '',
};
$self->setCollateral("Thingy_things", "thingId", $thing, 0, 1);
if($fields->rows < 1){
$self->session->log->warn("Thing failed to create because it had no fields");
my $i18n = WebGUI::International->new($self->session, "Asset_Thingy");
return $self->www_editThing($i18n->get("thing must have fields"));
}
while (my $field = $fields->hashRef) {
my $display = $self->session->form->process("display_".$field->{fieldId}) || 0;
my $viewScreenTitle = $self->session->form->process("viewScreenTitle_".$field->{fieldId}) || 0;
@ -2190,6 +2446,7 @@ sub www_editThingSave {
$self->session->db->write("update Thingy_fields set display = ?, viewScreenTitle = ?, displayinSearch = ?, searchIn = ? where fieldId = ? and thingId = ?",[$display, $viewScreenTitle, $displayInSearch, $searchIn, $field->{fieldId}, $thingId]);
}
$self->indexThing($thing);
return $self->www_manage;
}
#-------------------------------------------------------------------
@ -2208,6 +2465,7 @@ sub www_editField {
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->getId]);
if($session->form->process("copy")){
@ -2248,6 +2506,8 @@ sub www_editFieldSave {
if ($fieldType =~ m/^otherThing/){
$defaultValue = $session->form->process("defaultFieldInThing");
}
$thingId = $self->addThing({ thingId => 'new' },0) if $thingId eq 'new';
$fieldId = $session->form->process("fieldId");
%properties = (
@ -2285,12 +2545,12 @@ sub www_editFieldSave {
$newFieldId = $self->setCollateral("Thingy_fields","fieldId",\%properties,1,1,"thingId",$thingId);
}
if ($properties{fieldType} eq "File"){
$formElement = "<input type='file' name='file'>";
}
elsif ($properties{fieldType} eq "Image"){
if ($self->field_isa($properties{fieldType}, 'WebGUI::Form::Image')) {
$formElement = "<input type='file' name='image'>";
}
elsif ($self->field_isa($properties{fieldType}, 'WebGUI::Form::File')) {
$formElement = "<input type='file' name='file'>";
}
else{
$formElement = $self->getFormElement(\%properties);
}
@ -2303,15 +2563,16 @@ sub www_editFieldSave {
$listItemHTML = "<table>\n<tr>\n<td style='width:100px;' valign='top' class='formDescription'>".$label."</td>\n"
."<td style='width:370px;'>".$formElement."</td>\n"
."<td style='width:120px;' valign='top'> <input onClick=\"editListItem('".$session->url->page()
."?func=editField;fieldId=".$newFieldId.";thingId=".$properties{thingId}."','".$newFieldId."')\" value='".$i18n->get('Edit','Icon')."' type='button'>"
."<td style='width:120px;' valign='top'> <input onClick=\"editListItem('"
.$self->getUrl('func=editField;fieldId='.$newFieldId.';thingId='.$properties{thingId})
."','".$newFieldId."')\" value='".$i18n->get('Edit','Icon')."' type='button'>"
."<input onClick=\"deleteListItem('".$session->url->page()."','".$newFieldId
."','".$properties{thingId}."')\" value='".$i18n->get('Delete','Icon')."' type='button'></td>\n</tr>\n</table>";
# Make sure we send debug information along with the field.
$log->preventDebugOutput;
$session->output->print($newFieldId.$listItemHTML);
$session->output->print($thingId.$newFieldId.$listItemHTML);
return "chunked";
}
@ -2493,8 +2754,7 @@ sub editThingData {
." where thingDataId = ?",[$thingDataId]);
}
$fields = $session->db->read('select * from Thingy_fields where assetId = ? and thingId = ? order by sequenceNumber'
,[$self->getId,$thingId]);
$fields = $self->getFields($thingId);
while (my %field = $fields->hash) {
my $fieldName = 'field_'.$field{fieldId};
$fieldValue = undef;
@ -2656,13 +2916,13 @@ sub www_editThingDataSaveViaAjax {
#-------------------------------------------------------------------
=head2 www_export ( )
=head2 www_exportThing ( )
Exports search results as csv.
Exports one entire Thing as CSV.
=cut
sub www_export {
sub www_exportThing {
my $self = shift;
my $session = $self->session;
my ($query,$sth,$out,$fields,@fields,$fileName,@fieldLabels);
@ -2676,30 +2936,29 @@ sub www_export {
$pb->start($i18n->get('export label').' '.$thingProperties->{label}, $session->url->extras('assets/thingy.gif'));
$pb->update($i18n->get('Creating column headers'));
my $tempStorage = WebGUI::Storage->createTemp($session);
$fields = $session->db->read('select * from Thingy_fields where assetId =? and thingId = ? order by sequenceNumber',
[$self->getId,$thingId]);
$fields = $self->getFields($thingId);
while (my $field = $fields->hashRef) {
if ($field->{displayInSearch}){
push(@fields, {
fieldId => $field->{fieldId},
properties => $field,
});
push(@fieldLabels,$field->{label});
}
push(@fields, {
fieldId => $field->{fieldId},
properties => $field,
});
push(@fieldLabels,$field->{label});
}
my @metaDataFields = ('thingDataId','dateCreated','createdById','updatedById','updatedByName','lastUpdated','ipAddress');
if ($thingProperties->{exportMetaData}){
push(@fieldLabels,@metaDataFields)
}
$query = $session->cache->get("query_".$thingId);
$query = 'select * from '.$session->db->dbh->quote_identifier("Thingy_".$thingId);
$sth = $session->db->read($query);
### Loop through the returned structure and put it through Text::CSV
# Column heads
$self->session->log->warn("field labels: ". join ' ', @fieldLabels);
my $csv_filename = 'export_'.$thingProperties->{label}.'.csv';
$tempStorage->addFileFromScalar($csv_filename, WebGUI::Text::joinCSV(@fieldLabels));
open my $CSV, '>', $tempStorage->getPath($csv_filename);
print $CSV WebGUI::Text::joinCSV( @fieldLabels );
# Data lines
$pb->update($i18n->get('Writing data'));
@ -2712,11 +2971,11 @@ sub www_export {
my $value = $self->getFieldValue($data->{"field_".$fieldId},$field->{properties},"%y-%m-%d","%y-%m-%d %j:%n:%s");
push(@fieldValues, $value);
}
if ($thingProperties->{exportMetaData}) {
foreach my $metaDataField (@metaDataFields){
push(@fieldValues,$data->{$metaDataField});
}
}
if ($thingProperties->{exportMetaData}) {
push(@fieldValues, @{$data}{@metaDataFields});
}
print $CSV "\n".WebGUI::Text::joinCSV( @fieldValues );
#if (! ++$rowCounter % 25) {
$pb->update($i18n->get('Writing data'));
@ -2724,7 +2983,7 @@ sub www_export {
}
close $CSV;
$pb->update(sprintf q|<a href="%s">%s</a>|, $self->getUrl, sprintf($i18n->get('Return to %s'), $thingProperties->{label}));
$pb->update(sprintf q|<a href="%s">%s</a>|, $self->getUrl($self->getThingUrl($thingProperties)), sprintf($i18n->get('Return to %s'), $thingProperties->{label}));
return $pb->finish($tempStorage->getUrl($csv_filename));
}
@ -2758,8 +3017,7 @@ sub www_getThingViaAjax {
$thingProperties->{groupIdView});
my @field_loop;
my $fields = $session->db->read('select * from Thingy_fields where assetId=? and thingId=? order by sequenceNumber'
,[$self->getId,$thingId]);
my $fields = $self->getFields($thingId);
while (my $field = $fields->hashRef) {
$field->{formElement} = $self->getFormElement($field);
push(@field_loop,$field);
@ -2992,8 +3250,7 @@ sub www_importForm {
." <td>".$i18n->get("check duplicates label")."</td>"
."</tr>";
$fields = $db->read('select label, fieldId from Thingy_fields where assetId =? and thingId = ? order by sequenceNumber',
[$self->getId,$thingId]);
$fields = $self->getFields($thingId);
while (my $field = $fields->hashRef) {
$fieldOptions .= "<tr><td>".$field->{label}."</td><td>";
$fieldOptions .= WebGUI::Form::Checkbox->new($self->session, {
@ -3059,13 +3316,7 @@ sub www_manage {
'thing_searchUrl' => $session->url->append($url, 'func=search;thingId='.$thing->{thingId}),
);
# set the url for the view icon to the things default view
my $viewParams;
if ($thing->{defaultView} eq "addThing") {
$viewParams = 'func=editThingData;thingId='.$thing->{thingId}.';thingDataId=new';
}
else{
$viewParams = 'func=search;thingId='.$thing->{thingId};
}
my $viewParams = $self->getThingUrl($thing);
$templateVars{'thing_viewIcon'} = $session->icon->view($viewParams);
push (@things_loop, \%templateVars);
}
@ -3240,13 +3491,12 @@ sub getSearchTemplateVars {
my $session = $self->session;
my $dbh = $session->db->dbh;
my $i18n = WebGUI::International->new($self->session,"Asset_Thingy");
my ($var,$url,$orderBy);
my ($fields,@searchFields_loop,@displayInSearchFields_loop,$query,@constraints);
my (@searchResult_loop,$searchResults,@searchResults,@displayInSearchFields,$paginatePage,$currentUrl,$p);
my (@searchResult_loop,$searchResults,@searchResults,@displayInSearchFields,$paginatePage,$p);
$orderBy = $session->form->process("orderBy") || $thingProperties->{sortBy};
$var = $self->get;
$url = $self->getUrl;
my $orderBy = $session->form->process("orderBy") || $thingProperties->{sortBy};
my $var = $self->get;
my $url = $self->getUrl;
$var->{canEditThings} = $self->canEdit;
$var->{"addThing_url"} = $session->url->append($url, 'func=editThing;thingId=new');
@ -3254,7 +3504,7 @@ sub getSearchTemplateVars {
$var->{"thing_label"} = $thingProperties->{label};
if ($self->hasPrivileges($thingProperties->{groupIdExport})){
$var->{"export_url"} = $session->url->append($url, 'func=export;thingId='.$thingId);
$var->{"export_url"} = $session->url->append($url, 'func=exportThing;thingId='.$thingId);
}
if ($self->hasPrivileges($thingProperties->{groupIdImport})){
$var->{"import_url"} = $session->url->append($url, 'func=importForm;thingId='.$thingId);
@ -3265,21 +3515,17 @@ sub getSearchTemplateVars {
$var->{searchScreenTitle} = $thingProperties->{searchScreenTitle};
$var->{searchDescription} = $thingProperties->{searchDescription};
$currentUrl = $self->getUrl();
foreach ($self->session->form->param) {
# if we just saved data from an edit, we do not want to keep any of the params
last if $_ eq 'func' and $self->session->form->process($_) eq 'editThingDataSave';
unless ($_ eq "pn" || $_ eq "op" || $_ =~ /identifier/xi || $_ =~ /password/xi || $_ eq "orderBy" ||
$self->session->form->process($_) eq "") {
$currentUrl = $self->session->url->append($currentUrl,$self->session->url->escape($_)
.'='.$self->session->url->escape($self->session->form->process($_)));
}
my $currentUrl;
my $func = $session->form->process('func');
$func = 'search' if $func eq 'editThingDataSave';
$currentUrl = $self->getUrl('func='.$func.';thingId='.$thingId);
##Instead of blacklisting query params, they are whitelisted. List is currently empty.
FORM: foreach my $form (qw//) {
my $param = $session->form->process($form);
next FORM unless defined $param;
$currentUrl = $session->url->append($currentUrl, $form.'='.$param);
}
$fields = $session->db->read('select * from Thingy_fields where assetId =
'.$session->db->quote($self->getId).' and thingId = '.$session->db->quote($thingId).' order by
sequenceNumber');
$fields = $self->getFields($thingId);
while (my $field = $fields->hashRef) {
if ($field->{searchIn}){
my $searchForm = $self->getFormPlugin($field, 1);
@ -3332,7 +3578,8 @@ sequenceNumber');
}
$query .= join(", ",map {$dbh->quote_identifier('field_'.$_->{fieldId})} @displayInSearchFields);
$query .= " from ".$dbh->quote_identifier("Thingy_".$thingId);
if($session->form->process('func') eq 'search'){
my $func = $session->form->process('func');
if( $func eq 'search' || $func eq 'searchViaAjax' ){
# Don't add constraints when the search screen is displayed as an 'after save' option.
$query .= " where ".join(" and ",@constraints) if (scalar(@constraints) > 0);
}
@ -3345,9 +3592,6 @@ sequenceNumber');
$noFields = 1;
}
# store query in cache for thirty minutes
$self->session->cache->set("query_".$thingId, $query, 30*60);
$paginatePage = $self->session->form->param('pn') || 1;
$currentUrl = $self->session->url->append($currentUrl, "orderBy=".$orderBy) if $orderBy;
@ -3355,7 +3599,7 @@ sequenceNumber');
my @visibleResults;
if (! $noFields) {
my $sth = $self->session->db->read($query) if ! $noFields;
my $sth = $self->session->db->read($query);
while (my $result = $sth->hashRef){
if ($self->canViewThingData($thingId,$result->{thingDataId})){
push(@visibleResults,$result);
@ -3532,6 +3776,11 @@ The unique id of a thing.
The unique id of a row of thing data.
=head3 templateId
Optional. The unique id or url of the template to be used. When specified, the style template is not used.
If omitted, the thing data view template and style will be used.
=cut
sub www_viewThingData {
@ -3540,7 +3789,7 @@ sub www_viewThingData {
my $session = $self->session;
my $thingId = shift || $session->form->process('thingId');
my $thingDataId = shift || $session->form->process('thingDataId');
my $templateId = shift || $session->form->process('templateId');
my $var = $self->get;
my $url = $self->getUrl;
my $i18n = WebGUI::International->new($self->session, "Asset_Thingy");
@ -3570,9 +3819,19 @@ sub www_viewThingData {
$self->getViewThingVars($thingId,$thingDataId,$var);
$self->appendThingsVars($var, $thingId);
my $template;
if( $templateId )
{
$template = WebGUI::Asset::Template->newByUrl( $session, $templateId ) ||
WebGUI::Asset::Template->newById( $session, $templateId );
}
return $self->processStyle(
$self->processTemplate($var,$thingProperties->{viewTemplateId})
);
$self->processTemplate($var,$thingProperties->{viewTemplateId})
) if !$template;
return $self->processTemplate($var,$template->getId,$template);
}
#-------------------------------------------------------------------

View file

@ -20,6 +20,7 @@ use WebGUI::International;
use Moose;
use WebGUI::Definition::Asset;
extends 'WebGUI::Asset::Wobject';
with 'WebGUI::Role::Asset::Dashlet';
define tableName => 'WeatherData';
define assetName => ["assetName", 'Asset_WeatherData'];
define icon => 'weatherData.gif';
@ -57,6 +58,7 @@ property locations => (
tab => "properties",
hoverHelp => ["Your list of default weather locations", 'Asset_WeatherData'],
label => ["Default Locations", 'Asset_WeatherData'],
dashletOverridable => 1,
);
#-------------------------------------------------------------------
@ -93,49 +95,70 @@ to be displayed within the page style
sub view {
my $self = shift;
my $session = $self->session;
my %var;
my $url = $self->session->url;
if ($self->partnerId ne "" && $self->licenseKey ne "") {
foreach my $location (split("\n", $self->locations)) {
my $weather = Weather::Com::Finder->new({
'partner_id' => $self->partnerId,
'license' => $self->licenseKey,
'cache' => '/tmp',
});
next unless defined $weather;
my $overrides = $self->fetchUserOverrides($self->getParent->getId);
my $locations = $overrides->{locations} || $self->locations;
foreach my $location (split("\n", $locations)) {
my $loop_data;
my $link_data = [];
my $cached_data = $session->cache->get( join "", $self->getId, $location );
if ($cached_data) {
$loop_data = $cached_data->{locations};
$link_data = $cached_data->{links} || [];
}
else {
my $weather = Weather::Com::Finder->new({
'partner_id' => $self->partnerId,
'license' => $self->licenseKey,
'cache' => '/tmp',
});
next unless defined $weather;
foreach my $foundLocation(@{$weather->find($location)}) {
my $current_conditions = $foundLocation->current_conditions;
my $conditions = $current_conditions->description;
$conditions =~ s/\b(\w)/uc($1)/eg;
my $tempC = $current_conditions->temperature;
my $tempF;
$tempF = sprintf("%.0f",(((9/5)*$tempC) + 32)) if($tempC);
my $icon = $current_conditions->icon || "na";
foreach my $foundLocation(@{$weather->find($location)}) {
my $current_conditions = $foundLocation->current_conditions;
my $conditions = $current_conditions->description;
$conditions =~ s/\b(\w)/uc($1)/eg;
my $tempC = $current_conditions->temperature;
my $tempF;
$tempF = sprintf("%.0f",(((9/5)*$tempC) + 32)) if($tempC);
my $icon = $current_conditions->icon || "na";
push(@{$var{'ourLocations.loop'}}, {
query => $location,
cityState => $foundLocation->name || $location,
sky => $conditions || 'N/A',
tempF => (defined $tempF)?$tempF:'N/A',
tempC => (defined $tempC)?$tempC:'N/A',
smallIcon => $url->extras("wobject/WeatherData/small_icons/".$icon.".png"),
mediumIcon => $url->extras("wobject/WeatherData/medium_icons/".$icon.".png"),
largeIcon => $url->extras("wobject/WeatherData/large_icons/".$icon.".png"),
iconUrl => $url->extras("wobject/WeatherData/medium_icons/".$icon.".png"),
iconAlt => $conditions,
});
if (!$var{links_loop}) {
$var{links_loop} = [];
push @{$loop_data}, {
query => $location,
cityState => $foundLocation->name || $location,
sky => $conditions || 'N/A',
tempF => (defined $tempF)?$tempF:'N/A',
tempC => (defined $tempC)?$tempC:'N/A',
smallIcon => $url->extras("wobject/WeatherData/small_icons/".$icon.".png"),
mediumIcon => $url->extras("wobject/WeatherData/medium_icons/".$icon.".png"),
largeIcon => $url->extras("wobject/WeatherData/large_icons/".$icon.".png"),
iconUrl => $url->extras("wobject/WeatherData/medium_icons/".$icon.".png"),
iconAlt => $conditions,
last_fetch => time(),
};
for my $lnk (@{$foundLocation->current_conditions->{WEATHER}{lnks}{link}} ) {
push @{$var{links_loop}}, {
link_url => $lnk->{l},
link_title => $lnk->{t},
};
if (! $link_data) {
push @{ $link_data }, {
link_url => $lnk->{l},
link_title => $lnk->{t},
};
}
}
}
}
my $cached_data = {
locations => $loop_data,
links => $link_data,
};
$session->cache->set( join( "", $self->getId, $location ), $cached_data, $self->get('cacheTimeout'));
}
push @{$var{'ourLocations.loop'}}, @{ $loop_data };
if (!$var{links_loop}) {
$var{links_loop} = $link_data;
}
}
}
return $self->processTemplate(\%var, undef, $self->{_viewTemplate});

View file

@ -869,6 +869,8 @@ Return search results that match the keyword from the form variable C<keyword>.
sub www_byKeyword {
my $self = shift;
my $check = $self->checkView;
return $self->session->privilege->noAccess() unless $self->canView;
my $session = $self->session;
my $keyword = $session->form->process("keyword");
@ -980,6 +982,8 @@ Render a search form and process the contents, returning the results.
sub www_search {
my $self = shift;
my $check = $self->checkView;
return $self->session->privilege->noAccess() unless $self->canView;
my $i18n = WebGUI::International->new($self->session, "Asset_WikiMaster");
my $queryString = $self->session->form->process('query', 'text');
my $var = {

View file

@ -0,0 +1,129 @@
package WebGUI::AssetAspect::Dashlet;
use strict;
use Class::C3;
use JSON qw/to_json from_json/;
=head1 NAME
WebGUI::AssetAspect::Dashlet - Implement features to turn Assets into Dashlets
=head1 SYNOPSIS
This Aspect provides methods that allow a Dashboard to determine, store and retrieve
customization options for Assets.
=head1 DESCRIPTION
=head1 METHODS
#----------------------------------------------------------------------------
=head2 fetchUserOverrides ($dashboardAssetId, [$userId])
Retrieve user preferences for a particular dashboard and user for this Asset from the database.
=head3 $dashboardId
The assetId of the dashboard to reference.
=head3 $userId
The userId to whose preferences should be returned. Uses the current session user if omitted.
=cut
sub fetchUserOverrides {
my $self = shift;
my $dashboardAssetId = shift;
my $userId = shift || $self->session->user->userId;
my $properties_json = $self->session->db->quickScalar('select properties from Dashboard_userPrefs where dashboardAssetId=? and userId=? and dashletAssetId=?',[$dashboardAssetId, $userId, $self->getId,]);
$properties_json ||= '{}';
my $properties = from_json($properties_json);
return $properties;
}
#----------------------------------------------------------------------------
=head2 getOverrideFormDefinition
Return an array ref of form properties. The form properties are those that the
Asset has marked as being able to be overridden by a Dashboard asset by giving
the property the dashletOverridable flag.
Assets that want to allow additional properties outside of their definition should
override and extend this method.
=cut
sub getOverrideFormDefinition {
my $self = shift;
my $session = $self->session;
my @definitions = reverse @{ $self->definition($session) };
my @properties = ();
foreach my $definition (@definitions) {
foreach my $property_name (keys %{ $definition->{properties} }) {
my $property = $definition->{properties}->{$property_name};
next unless $property->{dashletOverridable};
$property->{name} = $property_name;
push @properties, $property;
}
}
return @properties;
}
#----------------------------------------------------------------------------
=head2 getUserOverrides
Store user preferences for this Asset. This is direct reference from inside the object, so
if you plan to modify the data, Clone it first.
=cut
sub getUserOverrides {
return shift->{_userOverrides};
}
#----------------------------------------------------------------------------
=head2 setUserOverrides
Store user preferences for this Asset.
=cut
sub setUserOverrides {
shift->{_userOverrides} = shift;
}
#----------------------------------------------------------------------------
=head2 storeUserOverrides ($dashboardAssetId, $properties, [$userId])
Store user preferences for a particular dashboard and user for this Asset to the database.
=head3 $dashboardId
The assetId of the dashboard to reference.
=head3 $userId
The userId to whose preferences should be returned. Uses the current session user if omitted.
=cut
sub storeUserOverrides {
my $self = shift;
my $session = $self->session;
my $dashboardAssetId = shift;
my $properties = shift;
my $userId = shift || $session->user->userId;
my $properties_json = to_json($properties);
$session->db->write('DELETE FROM Dashboard_userPrefs where dashboardAssetId=? and userId=? and dashletAssetId=?',[$dashboardAssetId, $userId, $self->getId]);
$session->db->write('INSERT INTO Dashboard_userPrefs (dashboardAssetId, userId, dashletAssetId, properties) VALUES (?,?,?,?)', [$dashboardAssetId, $userId, $self->getId, $properties_json]);
}
1; # You can't handle the truth

View file

@ -99,6 +99,10 @@ A hash reference of options that can modify how this method works.
Assets that normally autocommit their workflows (like CS Posts, and Wiki Pages) won't if this is true.
=head4 skipNotification
Disable sending a notification that a new revision was added, for those assets that support it.
=head4 state
A state for the duplicated asset (defaults to 'published')
@ -110,8 +114,15 @@ sub duplicate {
my $session = $self->session;
my $options = shift;
my $parent = $self->getParent;
##Remove state and pass all other options along to addChild
my $asset_state = delete $options->{state};
my $newAsset
= $parent->addChild( $self->get, undef, $self->get("revisionDate"), { skipAutoCommitWorkflows => $options->{skipAutoCommitWorkflows} } );
= $parent->addChild(
$self->get,
undef,
$self->get("revisionDate"),
$options,
);
if (! $newAsset) {
$self->session->log->error(
@ -120,12 +131,13 @@ sub duplicate {
return undef;
}
# Duplicate metadata fields
my $sth = $session->db->read(
"select * from metaData_values where assetId = ?",
[$self->getId]
my $sth = $self->session->db->read(
"select * from metaData_values where assetId = ? and revisionDate = ?",
[$self->getId, $self->get('revisionDate')]
);
while (my $h = $sth->hashRef) {
$session->db->write("insert into metaData_values (fieldId, assetId, value) values (?, ?, ?)", [$h->{fieldId}, $newAsset->getId, $h->{value}]);
$self->session->db->write("insert into metaData_values (fieldId,
assetId, revisionDate, value) values (?, ?, ?, ?)", [$h->{fieldId}, $newAsset->getId, $newAsset->get('revisionDate'), $h->{value}]);
}
# Duplicate keywords
@ -139,8 +151,8 @@ sub duplicate {
keywords => $keywords,
} );
if (my $state = $options->{state}) {
$newAsset->setState($state);
if ($asset_state) {
$newAsset->setState($asset_state);
}
return $newAsset;
@ -263,11 +275,12 @@ WebGUI::Fork method called by www_pasteList
sub pasteInFork {
my ( $process, $args ) = @_;
my $session = $process->session;
$session->log->info( "Trying " . $args->{assetId} );
my $self = WebGUI::Asset->newById( $session, $args->{assetId} );
$session->asset($self);
my @roots = grep { $_ && $_->canEdit }
map { WebGUI::Asset->newPending( $session, $_ ) } @{ $args->{list} };
map { $session->log->info( " Trying " . $_ ); WebGUI::Asset->newPending( $session, $_ ) } @{ $args->{list} };
my @ids = map {
my $list

View file

@ -39,10 +39,6 @@ use WebGUI::Exception;
use WebGUI::DateTime;
use WebGUI::Asset::Wobject::DataForm;
#readonly session => my %session;
#readonly assetId => my %assetId;
#readonly asset => my %asset;
has session => (
is => 'ro',
required => 1,
@ -101,13 +97,6 @@ has entryId => (
writer => '_set_entryId',
);
#private entryData => my %entryData;
#private entryId => my %entryId;
#private userId => my %userId;
#readonly username => my %username;
#public ipAddress => my %ipAddress;
#public submissionDate => my %submissionDate;
#-------------------------------------------------------------------
=head2 delete

View file

@ -379,8 +379,8 @@ sub exportBranch {
# try to write the file
eval { $asset->exportWriteFile };
if( $@ ) {
WebGUI::Error->throw(error => "could not export asset with URL " . $asset->getUrl . ": $@");
if( my $e = WebGUI::Error->caught() || $@ ) {
WebGUI::Error->throw(error => "could not export asset with URL " . $asset->getUrl . ": $e");
}
# next, tell the asset that we're exporting, so that it can export any
@ -419,7 +419,7 @@ sub exportBranch {
$asset->$report('done');
};
my $assetIds = $self->exportGetDescendants(undef, $depth);
my $assetIds = $self->exportGetAssetIds($options);
foreach my $assetId ( @{$assetIds} ) {
$exportAsset->( $assetId );
}
@ -507,6 +507,33 @@ sub exportCheckExportable {
#-------------------------------------------------------------------
=head2 exportGetAssetIds ( options )
Gets the ids of all the assets to be exported in this run as an arrayref.
Takes the same options spec as exportBranch.
=cut
sub exportGetAssetIds {
my ($self, $options) = @_;
my $session = $self->session;
my $ids = $self->exportGetDescendants( undef, $options->{depth} );
return $ids unless $options->{exportRelated};
# We want the ids in a descendant order, but we don't want to repeat
# assetIds, so we're using Tie::IxHash to get an ordered set.
tie my %set, 'Tie::IxHash';
while (my $id = shift @$ids) {
my $asset = WebGUI::Asset->newById($session, $id);
undef $set{$id};
for my $id (@{ $asset->exportGetRelatedAssetIds }) {
push(@$ids, $id) unless exists $set{$id};
}
}
return [ keys %set ];
}
#-------------------------------------------------------------------
=head2 exportGetDescendants ( user, depth )
Gets the descendants of this asset for exporting, walking the lineage as the
@ -587,6 +614,27 @@ sub exportGetDescendants {
#-------------------------------------------------------------------
=head2 exportGetRelatedAssetIds
Normally all an asset's shorcuts, but override if exporting your asset would
invalidate other exported assets. If exportRelated is checked, this will be
called and any assetIds it returns will be exported when your asset is
exported.
Note: You should NOT include parents as related assets simply because they're
your parents. If the user wants to export your parent, he can do that. This is
for assets that aren't necessarily in your ancestry. If parents were always
related, exporting anything would export everything.
=cut
sub exportGetRelatedAssetIds {
my $self = shift;
WebGUI::Asset::Shortcut->getShortcutsForAssetId($self->session, $self->getId);
}
#-------------------------------------------------------------------
=head2 exportGetUrlAsPath ( index )
Translates an asset's URL into an appropriate path and filename for exporting. For
@ -663,7 +711,7 @@ sub exportInFork {
my $session = $process->session;
my $self = WebGUI::Asset->newById( $session, delete $args->{assetId} );
$args->{indexFileName} = delete $args->{index};
my $assetIds = $self->exportGetDescendants( undef, $args->{depth} );
my $assetIds = $self->exportGetAssetIds($args);
my $tree = WebGUI::ProgressTree->new( $session, $assetIds );
$process->update( sub { $tree->json } );
my %reports = (

View file

@ -88,6 +88,12 @@ ENDHTML
name => "depth",
value => 99,
);
$f->addField( "YesNo",
label => $i18n->get('Export Related Assets'),
hoverHelp => $i18n->get('Export Related Assets description'),
name => "exportRelated",
value => '',
);
$f->addField( "selectBox",
label => $i18n->get('Export as user'),
hoverHelp => $i18n->get('Export as user description'),
@ -157,7 +163,7 @@ sub www_exportStatus {
unless $session->user->isInGroup(13);
my $form = $session->form;
my @vars = qw(
index depth userId extrasUploadsAction rootUrlAction exportUrl
index depth userId extrasUploadsAction rootUrlAction exportUrl exportRelated
);
$asset->forkWithStatusPage({
plugin => 'ProgressTree',

View file

@ -151,6 +151,7 @@ sub importProducts {
my %productRow;
##Order the data according to the headers, in whatever order they exist.
@productRow{ @headers } = @{ $productRow };
$productRow{price} =~ tr/0-9.//cd;
##Isolate just the collateral from the other product information
my %productCollateral;
@productCollateral{ @collateralFields } = @productRow{ @collateralFields };

View file

@ -783,19 +783,25 @@ Returns a 6 digit number with leading zeros of the next rank a child will get.
=cut
sub getNextChildRank {
my $self = shift;
my ($lineage) = $self->session->db->quickArray("select max(lineage) from asset where parentId=?",[$self->getId]);
my $rank;
if (defined $lineage) {
$rank = $self->getRank($lineage);
$self->session->log->fatal("Asset ".$self->getId." has too many children.") if ($rank >= 999998);
$rank++;
} else {
$rank = 1;
}
return $self->formatRank($rank);
}
my $self = shift;
# Increment by steps for servers in multi-master DB setups
my $inc_step = $self->session->config->get('db/increment_step') || 1;
my $inc_offset = $self->session->config->get('db/increment_offset') || 0;
my ($lineage) = $self->session->db->quickArray("select max(lineage) from asset where parentId=?",[$self->getId]);
my $rank;
if (defined $lineage) {
$rank = $self->getRank($lineage);
# Increase rank to next step then add offset
$rank += ( $inc_step - $rank % $inc_step ) + $inc_offset;
$self->session->log->fatal("Asset ".$self->getId." has too many children.") if ($rank >= 999999); # Each lineage area is only 6 digits
}
else {
$rank = 1;
}
return $self->formatRank($rank);
}
#-------------------------------------------------------------------

View file

@ -40,7 +40,8 @@ These methods are available from this class:
=head2 addMetaDataField ( )
Adds a new field to the metadata system, or edit an existing one.
Adds a new field to the metadata system, or edit an existing one. The id of
the field is returned.
=head3 fieldId
@ -68,6 +69,10 @@ The form field type for metaData: selectBox, text, integer, or checkList, yesNo,
For fields that provide options, the list of options. This is a string with
newline separated values.
=head3 classes
An arrayref of classnames that this metadata field applies to
=cut
sub addMetaDataField {
@ -79,18 +84,31 @@ sub addMetaDataField {
my $description = shift || '';
my $fieldType = shift;
my $possibleValues = shift;
my $classes = shift;
my $db = $self->session->db;
if($fieldId eq 'new') {
$fieldId = $self->session->id->generate();
$self->session->db->write("insert into metaData_properties (fieldId, fieldName, defaultValue, description, fieldType, possibleValues) values (?,?,?,?,?,?)",
$db->write("insert into metaData_properties (fieldId, fieldName, defaultValue, description, fieldType, possibleValues) values (?,?,?,?,?,?)",
[ $fieldId, $fieldName, $defaultValue, $description, $fieldType, $possibleValues, ]
);
}
else {
$self->session->db->write("update metaData_properties set fieldName = ?, defaultValue = ?, description = ?, fieldType = ?, possibleValues = ? where fieldId = ?",
$db->write("update metaData_properties set fieldName = ?, defaultValue = ?, description = ?, fieldType = ?, possibleValues = ? where fieldId = ?",
[ $fieldName, $defaultValue, $description, $fieldType, $possibleValues, $fieldId, ]
);
$db->write('delete from metaData_classes where fieldId=?', [$fieldId]);
}
if ($classes && @$classes) {
my $qfid = $db->quote($fieldId);
$db->write('insert into metaData_classes (fieldId, className) values '
.join(', ',
map { my $q = $db->quote($_); "($qfid, $q)" } @$classes
));
}
return $fieldId;
}
@ -109,12 +127,57 @@ The fieldId to be deleted.
sub deleteMetaDataField {
my $self = shift;
my $fieldId = shift;
$self->session->db->beginTransaction;
$self->session->db->write("delete from metaData_properties where fieldId = ?",[$fieldId]);
$self->session->db->write("delete from metaData_values where fieldId = ?",[$fieldId]);
$self->session->db->commit;
my $db = $self->session->db;
$db->beginTransaction;
for my $table (map { "metaData_$_" } qw(properties values classes)) {
$db->write("delete from $table where fieldId = ?", [ $fieldId ]);
}
$db->commit;
}
#-------------------------------------------------------------------
=head2 getMetaDataAsFormFields
Returns a hashref of metadata field names WebGUI::Form objects appropriate
for use on edit forms.
=cut
sub getMetaDataAsFormFields {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset');
my $fields = $self->getMetaDataFields;
my %hash;
for my $fid (keys %$fields) {
my $info = $fields->{$fid};
my $type = lcfirst ($info->{fieldType} || 'text');
my $name = $info->{fieldName};
my $options = $info->{possibleValues};
if($type eq 'selectBox') {
my $label = $i18n->get('Select');
$options = "|$label\n$options";
}
my $formClass = ucfirst $type;
$hash{$name} = WebGUI::Pluggable::instanciate(
"WebGUI::Form::$formClass",
'new',
[
$session, {
name => "metadata_$fid",
label => $name,
value => $info->{value},
extras => qq'title="$info->{description}"',
defaultValue => $info->{defaultValue},
fieldType => $type,
options => $options,
}
]
)->toHtml;
};
\%hash;
}
#-------------------------------------------------------------------
@ -140,21 +203,11 @@ sub getMetaDataAsTemplateVariables {
#-------------------------------------------------------------------
=head2 getMetaDataFields ( [fieldId] )
Returns a hash reference containing all metadata field properties for this Asset.
You can limit the output to a certain field by specifying a fieldId.
=head3 fieldId
If specified, the hashRef will contain only this field.
=cut
sub getMetaDataFields {
sub _getMetaDataFieldsHelper {
my $self = shift;
my $fieldId = shift;
my $session = $self->session;
my $listAll = shift || $fieldId;
my $db = $self->session->db;
my $sql = "select
f.fieldId,
f.fieldName,
@ -164,19 +217,76 @@ sub getMetaDataFields {
f.possibleValues,
d.value
from metaData_properties f
left join metaData_values d on f.fieldId=d.fieldId and d.assetId=".$session->db->quote($self->getId);
$sql .= " where f.fieldId = ".$session->db->quote($fieldId) if ($fieldId);
$sql .= " order by f.fieldName";
if ($fieldId) {
return $session->db->quickHashRef($sql);
}
else {
tie my %hash, 'Tie::IxHash';
%hash = %{ $session->db->buildHashRefOfHashRefs($sql, [], 'fieldId') };
return \%hash;
}
left join metaData_values d
on f.fieldId=d.fieldId
and d.assetId=?
and d.revisionDate = ?
";
my @where;
my @place = ($self->getId, $self->get('revisionDate'));
unless ($listAll) {
# Either there's no class info stored for this field or this class is
# one of them.
push @where, q{
not exists (
select * from metaData_classes where fieldId = f.fieldId
)
or exists (
select *
from metaData_classes
where className = ?
and fieldId = f.fieldId
)
};
push @place, ref $self;
}
if ($fieldId) {
push @where, 'f.fieldId = ?';
push @place, $fieldId;
}
if (@where) {
$sql .= 'where ' . join(' AND ', map { "($_)" } @where);
}
my $hash = $db->buildHashRefOfHashRefs( $sql, \@place, 'fieldId' );
return $fieldId ? $hash->{$fieldId} : $hash;
}
#-------------------------------------------------------------------
=head2 getAllMetaDataFields
getMetaDataFields without bothering about whether they apply to this class.
=cut
sub getAllMetaDataFields {
my $self = shift;
return $self->_getMetaDataFieldsHelper(undef, 1);
}
#-------------------------------------------------------------------
=head2 getMetaDataFields ( [fieldId] )
Returns a hash reference containing all metadata field properties for this Asset.
You can limit the output to a certain field by specifying a fieldId.
=head3 fieldId
If specified, the hashRef will contain only this field. In this case, you will
get that metadata field if it exists whether it applies to this asset or not.
=cut
sub getMetaDataFields {
my ($self, $fieldId) = @_;
return $self->_getMetaDataFieldsHelper($fieldId);
}
#-------------------------------------------------------------------
@ -198,17 +308,10 @@ sub updateMetaData {
my $self = shift;
my $fieldId = shift;
my $value = shift;
my $db = $self->session->db;
my ($exists) = $db->quickArray("select count(*) from metaData_values where assetId = ? and fieldId = ?",[$self->getId, $fieldId]);
if (!$exists && $value ne "") {
$db->write("insert into metaData_values (fieldId, assetId) values (?,?)",[$fieldId, $self->getId]);
}
if ($value eq "") { # Keep it clean
$db->write("delete from metaData_values where assetId = ? and fieldId = ?",[$self->getId, $fieldId]);
}
else {
$db->write("update metaData_values set value = ? where assetId = ? and fieldId=?", [$value, $self->getId, $fieldId]);
}
$self->session->db->write(
'replace into metaData_values (fieldId, assetId, revisionDate, value) values (?, ?, ?, ?)',
[$fieldId, $self->getId, $self->get('revisionDate'), $value]
);
}
@ -279,7 +382,49 @@ sub www_editMetaDataField {
value=>$fieldInfo->{fieldType} || "text",
types=> [ qw /text integer yesNo selectBox radioList checkList/ ]
);
$f->addField( "textarea",
my $default = ref WebGUI::Asset->assetName eq 'ARRAY'
? WebGUI::International->new( $self->session )->get( @{WebGUI::Asset->assetName} )
: WebGUI::Asset->assetName;
my %classOptions;
# usedNames maps a name to a class. If a name exists there, it has been
# used. If it maps to a classname, that classname needs to be renamed.
my %usedNames;
for my $class (WebGUI::Pluggable::findAndLoad('WebGUI::Asset')) {
next unless $class->isa('WebGUI::Asset');
my $name = ref $class->assetName eq 'ARRAY'
? WebGUI::International->new( $self->session )->get( @{$class->assetName} )
: $class->assetName;
next unless $name; # abstract classes (e.g. wobject) don't have names
# We don't want things named "Asset".
if ($name eq $default) {
$name = $class;
}
elsif (exists $usedNames{$name}) {
if (my $rename = $usedNames{$name}) {
$classOptions{$rename} = "$name ($rename)";
undef $usedNames{$name};
}
$name = "$name ($class)";
}
$usedNames{$name} = $class;
$classOptions{$class} = $name;
}
$f->addField( "selectList",
name => 'classes',
label => $i18n->get('Allowed Classes'),
hoverHelp => $i18n->get('Allowed Classes hoverHelp'),
options => \%classOptions,
defaultValue => $fid ne 'new' && $self->session->db->buildArrayRef(
'select className from metaData_classes where fieldId = ?',
[ $fid ]
),
sortByValue => 1,
);
$f->addField( "textarea",
name=>"possibleValues",
label=>$i18n->get(487),
hoverHelp=>$i18n->get('Possible Values description'),
@ -332,6 +477,7 @@ sub www_editMetaDataFieldSave {
$self->session->form->process("description") || '',
$self->session->form->process("fieldType"),
$self->session->form->process("possibleValues"),
[ $self->session->form->process("classes") ],
);
return $self->www_manageMetaData;
@ -353,7 +499,7 @@ sub www_manageMetaData {
my $i18n = WebGUI::International->new($self->session,"Asset");
$ac->addSubmenuItem($self->getUrl('func=editMetaDataField'), $i18n->get("Add new field"));
my $output;
my $fields = $self->getMetaDataFields();
my $fields = $self->getAllMetaDataFields;
foreach my $fieldId (keys %{$fields}) {
$output .= $self->session->icon->delete("func=deleteMetaDataField;fid=".$fieldId,$self->get("url"),$i18n->get('deleteConfirm'));
$output .= $self->session->icon->edit("func=editMetaDataField;fid=".$fieldId,$self->get("url"));

View file

@ -363,6 +363,8 @@ sub trash {
return 1;
}
#-------------------------------------------------------------------
require WebGUI::Workflow::Activity::DeleteExportedFiles;
sub _invokeWorkflowOnExportedFiles {
my $self = shift;

View file

@ -89,13 +89,33 @@ sub addRevision {
foreach my $table ($self->meta->get_tables) {
$session->db->write( "insert into ".$table." (assetId,revisionDate) values (?,?)", [$self->getId, $now]);
}
# Copy metadata values
my $db = $self->session->db;
my $id = $self->getId;
my $then = $self->get('revisionDate');
my $mdget = q{
select fieldId, value from metaData_values
where assetId = ? and revisionDate = ?
};
my $mdset = q{
insert into metaData_values (fieldId, value, assetId, revisionDate)
values (?, ?, ?, ?)
};
for my $row (@{ $db->buildArrayRefOfHashRefs($mdget, [ $id, $then ]) }) {
$db->write($mdset, [ $row->{fieldId}, $row->{value}, $id, $now ]);
}
$session->db->commit;
# current values, and the user set properties
my %mergedProperties = (%{$self->get}, %{$properties}, );
# Set some defaults
$mergedProperties{ revisedBy } ||= $session->user->userId;
$mergedProperties{ revisedBy } = $properties->{ revisedBy } || $session->user->userId;
# Force the packed head block to be regenerated
delete $mergedProperties{extraHeadTagsPacked};
#Instantiate new revision and fill with real data
my $newVersion = WebGUI::Asset->newById($session, $self->getId, $now);
@ -330,6 +350,10 @@ sub purgeRevision {
if ($count < 1) {
$db->write("update asset set isLockedBy=null where assetId=?",[$self->getId]);
}
$db->write(
'delete from metaData_values where assetId=? and revisionDate=?',
[ $self->getId, $self->get('revisionDate') ]
);
$db->commit;
$self->purgeCache;
$self->updateHistory("purged revision ".$self->get("revisionDate"));

View file

@ -900,7 +900,7 @@ sub www_createAccountSave {
my $username = $_[0];
my $properties = $_[1];
my $password = $_[2];
my $profile = $_[3];
my $profile = $_[3] || {};
my $i18n = WebGUI::International->new($self->session);
@ -910,6 +910,13 @@ sub www_createAccountSave {
my $userId = $u->userId;
$u->username($username);
$u->authMethod($self->authMethod);
$self->session->log->info( " override: " . $self->session->scratch->getLanguageOverride );
use Data::Dumper;
$self->session->log->info( Dumper $profile );
if (!$profile->{'language'} && $self->session->scratch->getLanguageOverride) {
$profile->{'language'} = $self->session->scratch->getLanguageOverride;
}
$u->karma($self->session->setting->get("karmaPerLogin"),"Login","Just for logging in.") if ($self->session->setting->get("useKarma"));
$u->updateProfileFields($profile) if ($profile);
$self->update($properties);

View file

@ -1127,7 +1127,13 @@ sub www_emailRecoverPasswordFinish {
my $mail = WebGUI::Mail::Send->create($session, { to=>$email, subject=>$i18n->get('WebGUI password recovery')});
my $vars = { };
$vars->{recoverPasswordUrl} = $session->url->append($session->url->getSiteURL,'op=auth;method=emailResetPassword;token='.$recoveryGuid);
my $template = WebGUI::Asset->newByDynamicClass($session, $session->setting->get('webguiPasswordRecoveryEmailTemplate'));
my $templateId = $session->setting->get('webguiPasswordRecoveryEmailTemplate');
my $template = WebGUI::Asset->newById($session, $templateId);
if (!$template) {
$session->errorHandler->error("Can't instantiate template $templateId for template email recovery");
my $i18n = WebGUI::International->new($self->session, 'Asset');
return $i18n->get('Error: Cannot instantiate template').' '.$templateId;
}
my $emailText = $template->process($vars);
WebGUI::Macro::process($session, \$emailText);
$mail->addText($emailText);

View file

@ -159,7 +159,7 @@ sub handler {
return undef unless ($form->get("op") eq "account");
#Visitor cannot access the acccount system
return $session->privilege->insufficient if($session->user->isVisitor);
return $session->privilege->noAccess if($session->user->isVisitor);
my $module = $form->get("module");
my $method = $form->get("do");

View file

@ -452,7 +452,7 @@ sub init {
$class->daemonize( $request, sub { $class->runCmd } );
};
}
exit 0;
CORE::exit(0);
} ## end sub init
#-----------------------------------------------------------------

View file

@ -145,6 +145,7 @@ sub renderBar {
my ( $process, $template, $extras ) = @_;
my $session = $process->session;
my $url = $session->url;
my $style = $session->style;
my $f = $session->form->paramsHashRef;
my $tt = Template->new;
my $dialog = delete $f->{dialog};
@ -211,13 +212,18 @@ sub renderBar {
return $styled;
}
else {
my $console = WebGUI::AdminConsole->new( $session, $f->{icon} );
my $style = $session->style;
$link->query_form($f);
$console->addSubmenuItem( $link->as_string, $label );
$style->setLink($_, { rel => 'stylesheet' }) for @sheets;
$style->setScript($_) for @scripts;
return $console->render( $content, $title );
if ( $session->var->isAdminOn ) {
my $console = WebGUI::AdminConsole->new( $session, $f->{icon} );
my $style = $session->style;
$link->query_form($f);
$console->addSubmenuItem( $link->as_string, $label );
return $console->render( $content, $title );
}
else {
return $session->style->userStyle( $content );
}
}
} ## end sub renderBar

View file

@ -154,7 +154,7 @@ my $template = <<'TEMPLATE';
}
tree = document.getElementById('tree');
tree.innerHTML = '';
_.each(JSON.parse(data.status), function (root) {
_.each(YAHOO.lang.JSON.parse(data.status), function (root) {
recurse(root, tree);
});
bar.update(finished, total);

View file

@ -100,4 +100,37 @@ sub getName {
return WebGUI::International->new($session, 'WebGUI')->get('codearea');
}
#-------------------------------------------------------------------
=head2 headTags
Includes script files for the code area
=cut
sub headTags {
my $self = shift;
my $session = $self->session;
$self->SUPER::headTags(@_);
$session->style->setScript(
$session->url->extras('yui-webgui/build/codeArea/codeArea-min.js')
);
}
#-------------------------------------------------------------------
=head2 toHtml
Add some javascript to fix hitting the tab key.
=cut
sub toHtml {
my $self = shift;
$self->headTags;
my $id = $self->get('id');
return $self->SUPER::toHtml(@_)
. qq{<script>new YAHOO.WebGUI.CodeArea('$id').render()</script>};
}
1;

View file

@ -245,7 +245,10 @@ sub toHtml {
my $originalValue = $self->getOriginalValue;
my $dt = eval { WebGUI::DateTime->new($session, $originalValue); };
$dt = WebGUI::DateTime->new($session,0) if ! (blessed $dt && $dt->isa('DateTime')); ##Parsing error
$dt->set_time_zone($session->datetime->getTimeZone);
if ($originalValue =~ $isaEpoch) {
##Epoch date, correct for time zone;
$dt->set_time_zone($session->datetime->getTimeZone);
}
$value = $dt->toMysqlDate;
}

View file

@ -141,6 +141,37 @@ sub getValueAsHtml {
}
#-------------------------------------------------------------------
=head2 headTags ( )
Set the head tags for this form plugin
=cut
sub headTags {
my $self = shift;
my $style = $self->session->style;
my $url = $self->session->url;
$style->setScript($url->extras('yui/build/yahoo/yahoo-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras('yui/build/dom/dom-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras('yui/build/event/event-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras("yui/build/datasource/datasource-min.js"), {type=>"text/javascript"});
$style->setScript($url->extras('yui/build/connection/connection-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras('yui/build/element/element-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras('yui/build/button/button-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras('yui/build/container/container-min.js'), { type=>'text/javascript' });
$style->setScript($url->extras("yui/build/autocomplete/autocomplete-min.js"), {type=>"text/javascript"});
$style->setScript($url->extras('yui-webgui/build/form/form.js'), { type=>'text/javascript' });
$style->setScript($url->extras('yui/build/json/json-min.js'), {type => 'text/javascript'});
$style->setScript($url->extras('yui-webgui/build/i18n/i18n.js'), {type => 'text/javascript'} );
$style->setScript($url->extras('yui-webgui/build/form/groupManager.js'), { type=>'text/javascript' });
$style->setLink($url->extras('yui/build/autocomplete/assets/skins/sam/autocomplete.css'), { rel => 'stylesheet', type => 'text/css' });
$style->setLink($url->extras('yui/build/container/assets/skins/sam/container.css'), { rel => 'stylesheet', type => 'text/css' });
$style->setLink($url->extras('yui/build/button/assets/skins/sam/button.css'), { rel => 'stylesheet', type => 'text/css' });
$style->setLink($url->extras('yui-webgui/build/form/groupManager.css'), { rel => 'stylesheet', type => 'text/css' });
}
#-------------------------------------------------------------------
=head2 isDynamicCompatible ( )
@ -180,9 +211,9 @@ Creates a series of hidden fields representing the data in the list.
=cut
sub toHtmlAsHidden {
my $self = shift;
$self->set("options", $self->session->db->buildHashRef("select groupId,groupName from groups"));
return $self->SUPER::toHtmlAsHidden();
my $self = shift;
$self->set("options", $self->session->db->buildHashRef("select groupId,groupName from groups"));
return $self->SUPER::toHtmlAsHidden();
}
#-------------------------------------------------------------------
@ -194,14 +225,187 @@ Renders the form field to HTML as a table row complete with labels, subtext, hov
=cut
sub toHtmlWithWrapper {
my $self = shift;
if ($self->session->user->isAdmin) {
my $subtext = $self->session->icon->manage("op=listGroups");
$self->set("subtext",$subtext . $self->get("subtext"));
my $self = shift;
my $session = $self->session;
my $user = $session->user;
if ($user->isAdmin) {
my $subtext = $session->icon->manage("op=listGroups");
$self->set("subtext", $subtext . $self->get("subtext"));
}
my $dialog = $self->get('name') . '_groupDialog';
my $group_manager = $user->isInGroup($session->setting->get('groupIdAdminGroup'));
my $form;
if ($group_manager) {
my $i18n = WebGUI::International->new($self->session,'Icon');
my $name = $self->get('name');
my $groupId = $self->getOriginalValue;
my $extra_subtext = qq!<span class="toolbarIcon" style="vertical-align:middle;"><a href="#" onClick="WebGUI.Form.GroupManager.show_dialog('$name'); return false;"><img src="!. $session->icon->getBaseURL().qq!edit.gif" alt="!. $i18n->get('Edit') .qq!"style="vertical-align:middle;border: 0px;" /></a></span>!;
$extra_subtext .= qq!<span class="toolbarIcon" style="vertical-align:middle;"><a href="#" onClick="WebGUI.Form.GroupManager.show_dialog('$name', 'new'); return false;"><img src="!. $session->icon->getBaseURL().qq!add.gif" alt="!. $i18n->get('Add') .qq!" style="vertical-align:middle;border: 0px;" /></a></span>!;
$self->set("subtext", $self->get('subtext').$extra_subtext);
}
$self->headTags;
$form .= $self->SUPER::toHtmlWithWrapper;
return $form;
}
#-------------------------------------------------------------------
=head2 www_groupMembers ($session)
Returns a list of users that are in the sub-group specified by the form variable
variable C<groupId>. Data returned is in JSON format.
This is a class method.
=head3 $session
A WebGUI::Session object.
=cut
sub www_groupMembers {
my $session = shift;
return '{}' unless $session->user->isInGroup($session->setting->get('groupIdAdminGroup'));
my $groupId = $session->form->param('groupId');
return '{}' unless $groupId;
my $group = WebGUI::Group->new($session, $groupId);
return '{}' unless $group;
my $results = {
groupName => $group->name,
users => [],
groups => [],
};
my $userIds = $group->getUsers('withoutExpired');
USER: foreach my $userId (@{ $userIds }) {
my $user = WebGUI::User->new($session, $userId);
next USER unless $user;
push @{$results->{users}},
{
username => $user->username,
userId => $userId,
};
}
my $groupIds = $group->getGroupsIn(0); ##Without recursion
GROUP: foreach my $groupId (@{ $groupIds }) {
my $group = WebGUI::Group->new($session, $groupId);
next GROUP unless $group;
push @{$results->{groups}},
{
groupName => $group->name,
groupId => $groupId,
};
}
return JSON::to_json($results);
}
#-------------------------------------------------------------------
=head2 www_saveGroup ($session)
Save new information about the membership of a group, which users have
been added and deleted, and which groups have been added and deleted.
This is a subroutine, not a class method, not an object method.
=head3 $session
A WebGUI::Session object.
=head3 Expected form variables
=head4 groupId
The GUID for the group to modify.
=head4 groupName
The name of the group. This is always set, so it should always be included.
=head4 usersAdded
A list of userId's for users who were added.
=head4 usersDeleted
A list of userId's for users who were deleted. Deleting happens after adding.
=head4 groupsAdded
A list of groupId's for groups who were added.
=head4 groupsDeleted
A list of groupId's for groups who were deleted. Deleting happens after adding.
=cut
sub www_saveGroup {
my $session = shift;
$session->log->warn("hit the group plugin");
return '{}' unless $session->user->isInGroup($session->setting->get('groupIdAdminGroup'));
my $form = $session->form;
my $groupId = $form->get('groupId');
my $group = WebGUI::Group->new($session, $groupId);
$session->log->warn("got groupId: $groupId");
return '{}' unless $group;
$session->log->warn("updating group data, name = ".$form->get('groupName'));
$group->name($form->get('groupName'));
my @usersAdded = $form->get('usersAdded');
use Data::Dumper;
$session->log->warn("users added ".Dumper(\@usersAdded));
$group->addUsers(\@usersAdded);
my @usersDeleted = $form->get('usersDeleted');
$group->deleteUsers(\@usersDeleted);
my @groupsAdded = $form->get('groupsAdded');
$session->log->warn("groups added ".Dumper(\@groupsAdded));
$group->addGroups(\@groupsAdded);
my @groupsDeleted = $form->get('groupsDeleted');
$group->deleteGroups(\@groupsDeleted);
return JSON::to_json({ groupId => $group->getId, groupName => $group->name, originalGroupId => $groupId });
}
#-------------------------------------------------------------------
=head2 www_searchGroups ($session)
Returns groups that match the supplied group name. Group name is specified via the form
variable C<search>. A list of groups will be returned of up to 15 names and groupIds.
This is a subroutine, not a class method, not an object method.
=head3 $session
A WebGUI::Session object.
=head3 Sample JSON
{
'results' : [
{
'groupId': 'someGroupId',
'groupName' : 'Great Group'
}
return $self->SUPER::toHtmlWithWrapper;
//Other hashes may be in the list, or it could be completely empty
]
}
=cut
sub www_searchGroups {
my $session = shift;
return '{"results":[]}' unless $session->user->isInGroup($session->setting->get('groupIdAdminGroup'));
my $search = $session->form->param('query');
my $results = $session->db->buildArrayRefOfHashRefs(q|select groupId, groupName from groups where groupName like CONCAT(?, '%') and showInForms=1 LIMIT 15|, [ $search ]);
return JSON::to_json({ results => $results });
}
1;

View file

@ -139,6 +139,8 @@ sub headTags {
my ( $url, $style ) = $self->session->quick(qw( url style ));
$style->setScript( $url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'));
$style->setScript( $url->extras('yui/build/json/json-min.js'));
$style->setScript( $url->extras('yui/build/connect/connect-min.js') );
$style->setScript( $url->extras('yui-webgui/build/i18n/i18n.js') );
$style->setScript( $url->extras('yui-webgui/build/form/jsontable.js'));
}
@ -153,6 +155,7 @@ Renders an input tag of type text.
sub toHtml {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'WebGUI');
my ( $url, $style ) = $session->quick(qw( url style ));
my $value = $self->fixMacros($self->fixQuotes($self->fixSpecialCharacters($self->getOriginalValue)));
my $output = '';
@ -167,7 +170,7 @@ sub toHtml {
# Buttons to add rows in the table footer
my $cols = scalar @{ $self->get('fields') } + 1; # Extra column for buttons
$output .= '</thead><tfoot><tr><td colspan="' . $cols . '">'
. '<button id="' . $self->get('id') . '_add">' . "Add" . '</button>'
. '<button id="' . $self->get('id') . '_add">' . $i18n->get('Add') . '</button>'
. '</td></tr></tfoot>'
;
@ -194,7 +197,10 @@ sub toHtml {
elsif ( $field->{type} eq "id" ) {
$fieldHtml .= '<input type="hidden" class="jsontable_id" name="' . $fieldName . '" value="new" />';
}
else { # Readonly or unknown
elsif ( $field->{type} eq "hidden" || $field->{type} eq "readonly" ) {
$fieldHtml .= '<input type="hidden" name="' . $fieldName . '" value="new" />';
}
else { # Unknown
$fieldHtml = '&nbsp;';
}

View file

@ -0,0 +1,122 @@
package WebGUI::Form::TemplateParser;
=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::SelectBox';
use WebGUI::International;
use Tie::IxHash;
=head1 NAME
Package WebGUI::Form::TemplateParser
=head1 DESCRIPTION
A dropdown list for selecting a template parser.
=head1 SEE ALSO
This is a subclass of WebGUI::Form::SelectBox.
=head1 METHODS
The following methods are specifically available from this class. Check the superclass for additional methods.
=cut
#-------------------------------------------------------------------
=head2 areOptionsSettable
No, they aren't.
=cut
sub areOptionsSettable { 0 }
#----------------------------------------------------------------------------
=head2 definition ( [ additionalTerms ] )
See the super class for additional details.
=head3 additionalTerms
The following additional parameters have been added via this sub class.
=head3 allowNone
Set to true if "None" is an acceptable option for this dropdown. Defaults to
false.
=head4 defaultValue
Defaults to the default parser selected in the config file
=cut
sub definition {
my ($class, $session, $definition) = @_;
push @{$definition ||= []}, {
allowNone => {
defaultValue => 0,
},
defaultValue => {
defaultValue => $session->config->get('defaultTemplateParser')
}
};
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getName ( session )
Returns the human readable name of this control.
=cut
sub getName {
my ($self, $session) = @_;
return WebGUI::International->new($session, 'WebGUI')->get('Template Parser');
}
#-------------------------------------------------------------------
=head2 getOptions
Called by the superclass to determine which options are presented.
=cut
sub getOptions {
my $self = shift;
my $session = $self->session;
tie my %o, 'Tie::IxHash';
if ($self->get('allowNone')) {
$o{''} = WebGUI::International->new($session, 'WebGUI')->get('881');
}
return \%o unless my $parsers = $session->config->get('templateParsers');
for my $class (@$parsers) {
my $parser = WebGUI::Asset::Template->getParser($session, $class);
$o{$class} = $parser->getName;
}
return \%o;
}
1;

View file

@ -157,6 +157,32 @@ sub getValue {
#-------------------------------------------------------------------
=head2 getValueAsHtml ( )
Return the Form's value as a formatted time.
=cut
sub getValueAsHtml {
my $self = shift;
my $value = $self->getOriginalValue();
my $mysqlTime = ($value =~ $mysqlFormattedDate);
my $digits = ($value =~ /^\d+$/);
##Format is fine
if ( $mysqlTime ) {
return $value;
}
##Convert to mysql format
elsif ($digits) {
return $self->session->datetime->secondsToTime($value);
}
else { ##Bad stuff, maynard
return undef;
}
}
#-------------------------------------------------------------------
=head2 headTags ( )
Set the head tags for this form plugin
@ -164,8 +190,15 @@ Set the head tags for this form plugin
=cut
sub headTags {
my $self = shift;
$self->session->style->setScript($self->session->url->extras('inputCheck.js'));
my $self = shift;
my $style = $self->session->style;
my $url = $self->session->url;
$style->setScript($url->extras('inputCheck.js'), { type => 'text/javascript' });
$style->setScript($url->extras('yui/build/connection/connection-min.js'), { type => 'text/javascript'});
$style->setScript($url->extras('yui/build/event/event-min.js'), { type => 'text/javascript' });
$style->setScript($url->extras('yui/build/json/json-min.js'), { type => 'text/javascript' });
$style->setScript($url->extras('yui-webgui/build/i18n/i18n.js' ), { type => 'text/javascript' });
$style->setScript($url->extras('form/timefield.js'), { type => 'text/javascript' });
}
#-------------------------------------------------------------------
@ -190,15 +223,11 @@ Renders a time field.
sub toHtml {
my $self = shift;
my $value = $self->getOriginalValue;
my $i18n = WebGUI::International->new($self->session);
$self->set("extras", $self->get('extras') . ' onkeyup="doInputCheck(document.getElementById(\''.$self->get("id").'\'),\'0123456789:\')"');
return $self->SUPER::toHtml
.WebGUI::Form::Button->new($self->session,
id=>$self->get('id'),
extras=>'style="font-size: 8pt;" onclick="window.timeField = this.form.'.$self->get("name").';clockSet = window.open(\''.$self->session->url->extras('timeChooser.html').'\',\'timeChooser\',\'WIDTH=230,HEIGHT=100\');return false"',
value=>$i18n->get(970)
)->toHtml;
##JS expects formatted time
$self->set('value', $self->getValueAsHtml);
#my $i18n = WebGUI::International->new($self->session);
$self->set("extras", $self->get('extras') . ' onblur="WebGUI.TimeField.munge(document.getElementById(\''.$self->get("id").'\'))" onkeyup="WebGUI.TimeField.check(document.getElementById(\''.$self->get("id").'\'));"');
return $self->SUPER::toHtml;
}
#-------------------------------------------------------------------

View file

@ -170,5 +170,25 @@ sub toHtml {
)->toHtml).$manage;
}
#-------------------------------------------------------------------
=head2 www_searchUsers
Returns users that match the supplied username. Username is specified via the form
variable C<search>. A list of usernames will be returned of up to 15 names and userIds.
=cut
sub www_searchUsers {
my $session = shift;
return '{"results":[]}' unless $session->user->isInGroup($session->setting->get('groupIdAdminUser'));
my $search = $session->form->param('query');
my $results = $session->db->buildArrayRefOfHashRefs(q|select userId, username from users where username like CONCAT(?, '%') LIMIT 15|, [ $search ]);
return JSON::to_json({ results => $results });
}
1;

View file

@ -92,9 +92,8 @@ An optional value to use instead of POST input.
sub getValue {
my $self = shift;
my $value = $self->SUPER::getValue(@_);
my $value = uc $self->SUPER::getValue(@_);
$value =~ tr/\r\n//d;
$value =~ tr/a-z/A-Z/;
if ($value =~ /^[A-Z\d\s\-]+$/) {
return $value;
}

View file

@ -180,7 +180,7 @@ sub addGroups {
my $group = WebGUI::Group->new($self->session, $gid);
my $recursive = $self->getId ~~ $group->getGroupsIn(1);
next GROUP if $recursive;
$self->session->db->write("insert into groupGroupings (groupId,inGroup) values (?,?)",[$gid, $self->getId]);
$self->session->db->write("REPLACE into groupGroupings (groupId,inGroup) values (?,?)",[$gid, $self->getId]);
}
$self->clearCaches();
return 1;
@ -212,7 +212,7 @@ sub addUsers {
foreach my $uid (@{$users}) {
my ($isIn) = $self->session->db->quickArray("select count(*) from groupings where groupId=? and userId=?", [$self->getId, $uid]);
unless ($isIn) {
$self->session->db->write("insert into groupings (groupId,userId,expireDate) values (?,?,?)", [$self->getId, $uid, (time()+$expireOffset)]);
$self->session->db->write("REPLACE into groupings (groupId,userId,expireDate) values (?,?,?)", [$self->getId, $uid, (time()+$expireOffset)]);
$self->session->stow->delete("gotGroupsForUser");
} else {
$self->userGroupExpireDate($uid,(time()+$expireOffset));
@ -1007,24 +1007,33 @@ sub getUsersNotIn {
if($groupId eq "") {
return $self->getUsers($withoutExpired);
}
my $selfWhere;
if ( $self->getId ne '2' ) {
$selfWhere = "and groupId=" . $self->session->db->dbh->quote( $self->getId );
}
else {
$selfWhere = 'and userId != ' . $self->session->db->dbh->quote( "1" );
}
my $expireTime = 0;
if ($withoutExpired) {
$expireTime = time();
}
my $sql = q{
my $sql = qq{
select
userId
from
groupings
users
left join
groupings using (userId)
where
expireDate > ?
and groupId=?
$selfWhere
and userId not in (select userId from groupings where expireDate > ? and groupId=?)
};
my @users = $self->session->db->buildArray($sql, [$expireTime,$self->getId,$expireTime,$groupId]);
my @users = $self->session->db->buildArray($sql, [$expireTime,$expireTime,$groupId]);
return \@users;
}

View file

@ -0,0 +1,60 @@
package WebGUI::Help::Asset_Dashboard;
use strict;
our $HELP = {
'dashboard template' => {
title => 'Dashboard Template Variables',
isa => [
{ namespace => "Asset_Dashboard",
tag => "dashboard asset template variables"
},
{ namespace => "Asset",
tag => "asset template"
},
],
fields => [],
variables => [
{ name => 'dragger.init' },
{ name => 'fullUrl' },
{ name => 'canEdit' },
{ name => 'positionN_loop',
variables => [
{ 'name' => 'id' },
{ 'name' => 'content' },
{ 'name' => 'dashletTitle' },
{ 'name' => 'shortcutUrl' },
{ 'name' => 'dashletUrl' },
{ 'name' => 'canDelete' },
{ 'name' => 'canMove' },
{ 'name' => 'canPersonalize' },
{ 'name' => 'showReloadIcon' },
{ 'name' => 'canEditUserPrefs' },
{ 'name' => 'editFormUrl' },
]
},
],
related => []
},
'dashboard asset template variables' => {
private => 1,
title => 'dashboard asset template variables title',
isa => [
{ namespace => "Asset_Wobject",
tag => "wobject template variables"
},
],
fields => [],
variables => [
{ name => 'templateId' },
{ name => 'adminsGroupId' },
{ name => 'usersGroupId' },
{ name => 'isInitialized' },
],
related => []
},
};
1;

View file

@ -0,0 +1,59 @@
package WebGUI::Help::Asset_Map;
use strict;
our $HELP = {
'view template' => {
title => 'view template',
body => '',
isa => [
{ namespace => 'Asset_Template',
tag => 'template variables'
},
{ namespace => 'Asset_Map',
tag => 'map asset template variables'
},
],
fields => [],
variables => [
{ name => 'canAddPoint', },
{ name => 'canEdit', },
{ name => 'mapPoints', required => 1, },
{ name => 'button_addPoint', required => 1, },
{ name => 'button_setCenter', required => 1, },
{ name => 'button_setCenter', selectPoint => 1, },
],
related => []
},
'map asset template variables' => {
private => 1,
title => 'map asset template variables',
body => '',
isa => [
{ namespace => 'Asset',
tag => 'asset template asset variables'
},
],
fields => [],
variables => [
{ name => 'groupIdAddPoint', },
{ name => 'mapApiKey', },
{ name => 'mapHeight', },
{ name => 'mapWidth', },
{ name => 'startLatitude', },
{ name => 'startLongitude', },
{ name => 'startZoom', },
{ name => 'templateIdEditPoint', },
{ name => 'templateIdView', },
{ name => 'templateIdViewPoint', },
{ name => 'workflowIdPoint', },
{ name => 'canAddPoint', },
{ name => 'canEdit', },
],
related => []
},
};
1;

View file

@ -10,6 +10,9 @@ our $HELP = {
{ namespace => 'Asset_Template',
tag => 'template variables'
},
{ namespace => 'Asset_MapPoint',
tag => 'map point asset template variables'
},
],
fields => [],
variables => [
@ -31,6 +34,9 @@ our $HELP = {
{ name => 'form_phone', },
{ name => 'form_fax', },
{ name => 'form_email', },
{ name => 'user defined variables', },
{ name => 'form_isHidden', },
{ name => 'form_isGeocoded', },
],
related => []
},
@ -60,6 +66,11 @@ our $HELP = {
{ name => 'phone', },
{ name => 'fax', },
{ name => 'email', },
{ name => 'userDefined1', },
{ name => 'userDefined2', },
{ name => 'userDefined3', },
{ name => 'userDefined4', },
{ name => 'userDefined5', },
],
related => []
},

View file

@ -37,6 +37,7 @@ our $HELP = {
{ name => 'imgUrl', },
{ name => 'imgFilename', },
{ name => 'newUploadForm', },
{ name => 'imgRemoteUrlForm', },
{ name => 'imgCaptionForm', },
{ name => 'imgBylineForm', },
{ name => 'imgAltForm', },

View file

@ -29,6 +29,7 @@ our $HELP = {
{ name => 'editIcon' },
],
},
{ name => 'topStory' },
{ name => 'topStoryDeleteIcon',
description => 'deleteIcon', },
{ name => 'topStoryEditIcon',
@ -46,7 +47,12 @@ our $HELP = {
{ name => 'rssUrl' },
{ name => 'atomUrl' },
],
related => []
related => [
{
namespace => 'Asset_Story',
tag => 'view template',
}
],
},
'storytopic asset template variables' => {

View file

@ -39,6 +39,7 @@ our $HELP = {
{ 'name' => 'category' },
{ 'name' => 'author' },
{ 'name' => 'guid' },
{ 'name' => 'media' },
{ 'name' => 'description' },
{ 'name' => 'descriptionFirst100words' },
{ 'name' => 'descriptionFirst75words' },

View file

@ -152,6 +152,9 @@ our $HELP = {
{ 'name' => 'field_pretext' },
],
},
{
'name' => 'variables by label',
},
],
related => [
{ tag => 'edit thing template',

View file

@ -740,6 +740,9 @@ our $HELP = {
{
name => 'viewItemUrl',
},
{
name => 'hasSku',
},
{
name => 'price',
description => 'price help',

43
lib/WebGUI/ICal.pm Normal file
View file

@ -0,0 +1,43 @@
package WebGUI::ICal;
=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
-------------------------------------------------------------------
=head1 NAME
Package WebGUI::ICal
=head1 DESCRIPTION
This package wraps Data::ICal so the PRODUCT_ID parameter in the generated iCal feeds are set appropriately.
=head1 SYNOPSIS
use WebGUI::ICal;
=cut
use WebGUI;
use base 'Data::ICal';
=head2 product_id
Override the method from Data::ICal to set it to be the WebGUI version and status.
=cut
sub product_id {
return 'WebGUI '. $WebGUI::VERSION . '-' . $WebGUI::STATUS;
}
1;

View file

@ -35,8 +35,6 @@ This package provides an interface to the internationalization system.
$string = $i->get($internationalId);
$string = $i->get($internationalId, $otherNamespace);
$url = $i->makeUrlCompliant($url);
$hashRef = $i->getLanguage($lang);
$hashRef = $i->getLanguages();
@ -206,6 +204,7 @@ sub getLanguages {
my ($self) = @_;
my $hashRef;
for my $lang ( findsubmod 'WebGUI::i18n' ) {
$self->session->log->info( "Found language $lang" );
$lang =~ s/^WebGUI::i18n:://;
$hashRef->{$lang} = $self->getLanguage($lang, "label");
}
@ -217,6 +216,8 @@ sub getLanguages {
=head2 makeUrlCompliant ( url [ , language ] )
THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM WEBGUI.
Manipulates a URL to make sure it will work on the internet. It removes things like non-latin characters, etc.
=head3 url
@ -231,11 +232,7 @@ Specify a default language. Defaults to user preference or "English".
sub makeUrlCompliant {
my ($self, $url, $language) = @_;
$language = $language || $self->{_language} || $self->session->user->get("language") || "English";
my $cmd = "WebGUI::i18n::".$language;
WebGUI::Pluggable::load($cmd);
my $output = WebGUI::Pluggable::run($cmd, 'makeUrlCompliant', [$url]);
return $output;
return $url;
}

View file

@ -228,11 +228,20 @@ sub generateCloud {
[ $options->{startAsset}->get("lineage").'%', @extraPlaceholders, $maxKeywords ]);
my $cloud = HTML::TagCloud->new(levels=>$options->{cloudLevels} || 24);
while (my ($count, $keyword) = $sth->array) {
my $url
= $urlCallback ? $display->$urlCallback($keyword)
: $options->{displayFunc} ? $display->getUrl("func=".$options->{displayFunc}.";keyword=".$keyword)
: $display->getUrl("keyword=".$keyword)
;
my $url;
if ($urlCallback) {
$url = $display->$urlCallback($keyword);
}
else {
my @q = ( [ 'keyword', $keyword, ] );
my $e = $self->session->url;
if (my $func = $options->{displayFunc}) {
unshift @q, [ 'func', $func ];
}
$url = $display->getUrl(
join(';', map { join '=', $_->[0], $e->escape($_->[1]) } @q)
);
}
$cloud->add($keyword, $url, $count);
}
return $cloud->html_and_css($maxKeywords);

View file

@ -43,6 +43,15 @@ Defaults to 'url'. But if you want to use an assetId as the first parameter, the
#-------------------------------------------------------------------
sub process {
my ($session, $identifier, $type) = @_;
if (!$identifier) {
$session->errorHandler->warn('AssetProxy macro called without an asset to proxy. '
. 'The macro was called through this url: '.$session->asset->get('url'));
if ($session->isAdminOn) {
my $i18n = WebGUI::International->new($session, 'Macro_AssetProxy');
return $i18n->get('invalid url');
}
return;
}
my $perfLog = $session->log->performanceLogger;
my $t = $perfLog ? [Time::HiRes::gettimeofday()] : undef;
my $asset;

View file

@ -46,14 +46,17 @@ sub process {
my $asset = eval { WebGUI::Asset->newByUrl($session,$url); };
my $i18n = WebGUI::International->new($session, 'Macro_FileUrl');
if (Exception::Class->caught()) {
$session->log->warn("Invalid Asset URL for url: " . $url);
return $i18n->get('invalid url');
}
my $storageId = $asset->can('storageId') ? $asset->storageId : undef;
if (not defined $storageId) {
$session->log->warn("No Storage Location for assetId: " . $asset->getId . " url: $url");
return $i18n->get('no storage');
}
my $filename = $asset->can('filename') ? $asset->filename : undef;
if (not defined $filename) {
$session->log->warn("No Filename for assetId: " . $asset->getId . " url: $url");
return $i18n->get('no filename');
}
my $storage = WebGUI::Storage->get($session,$storageId);

View file

@ -0,0 +1,48 @@
package WebGUI::Macro::LastUpdatedBy;
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use strict;
use WebGUI::Asset;
use WebGUI::User;
=head1 NAME
Package WebGUI::Macro::LastUpdatedBy
=head1 DESCRIPTION
Macro for displaying the username of the user that made the most recent revision of current Asset.
=head2 process ( )
Display the username, if the user still exists in the system. If not, of if the user does not
have a username, then display an internationalized label for "Unknown".
=cut
#-------------------------------------------------------------------
sub process {
my $session = shift;
return '' unless $session->asset;
my $userId = $session->asset->getContentLastModifiedBy();
my $user = WebGUI::User->new($session, $userId);
if ($user && $user->username) {
return $user->username;
}
my $i18n = WebGUI::International->new($session,'Macro_LastModified');
return $i18n->get('Unknown');
}
1;

View file

@ -0,0 +1,66 @@
package WebGUI::Macro::RenderThingData;
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2011 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 WebGUI::Group;
use WebGUI::Asset::Template;
use WebGUI::Asset::Wobject::Thingy;
=head1 NAME
Package WebGUI::Macro::RenderThingData
=head1 DESCRIPTION
Macro that allows users to render thing data.
=head2 process ( thingURL, templateHint )
=head3 thingHint
The URL from which to pull the thingId and thingDataId
=head3 templateHint
Optional. Specifies the templateId or template url to use. If omitted, the default thingy view template will be used.
=cut
#-------------------------------------------------------------------
sub process {
my ($session, $thingDataUrl, $templateHint ) = @_;
my $uri = URI->new( $thingDataUrl );
my $urlHash = { $uri->query_form };
my $thingId = $urlHash->{'thingId'};
my $thingDataId = $urlHash->{'thingDataId'};
my $thing = WebGUI::Asset::Wobject::Thingy->newByUrl( $session, $uri->path );
# TODO: i18n
return ( "Bad URL: " . $thingDataUrl ) if !$thing || !$thingId || !$thingDataId;
# Render
my $output = $thing->www_viewThingData( $thingId, $thingDataId, $templateHint );
# FIX: Temporary solution (broken map due to template rendering <script> tags)
return "RenderThingData: Please specify a template." if !$templateHint;
return "RenderThingData: Contained bad tags!" if $output =~ /script>/;
return $output;
}
1;

View file

@ -60,6 +60,9 @@ sub call {
$privs = JSON->new->utf8->decode($contents);
}
return @$r = (403, [ 'Content-Type' => 'text/plain' ], [ 'Forbidden' ])
if $privs->{state} eq 'trash';
require WebGUI::Asset;
my $userId = $session->get('userId');

View file

@ -130,6 +130,8 @@ sub getOperations {
'listGroups' => 'Group',
'manageGroupsInGroup' => 'Group',
'manageUsersInGroup' => 'Group',
'manageGroups' => 'Group',
'updateGroupUsers' => 'Group',
'viewHelp' => 'Help',
'viewHelpIndex' => 'Help',

View file

@ -826,6 +826,11 @@ sub www_emailGroup {
-label=>$i18n->get(229),
-hoverHelp=>$i18n->get('229 description'),
);
$f->yesNo(
-name=>'override',
-label=>$i18n->get('override user email preference'),
-hoverHelp=>$i18n->get('override user email preference description'),
);
$f->HTMLArea(
-name=>"message",
-label=>$i18n->get(230),
@ -851,11 +856,21 @@ A WebGUI::Session object
sub www_emailGroupSend {
my $session = shift;
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")) && $session->form->validToken);
my $mail = WebGUI::Mail::Send->create($session, {toGroup=>$session->form->process("gid"),subject=>$session->form->process("subject"),from=>$session->form->process("from")});
$mail->addHtml($session->form->process("message","HTMLArea"));
$mail->addFooter;
$mail->queue;
my $f = $session->form;
return $session->privilege->adminOnly()
unless (canEditGroup($session,$f->get('gid')) && $f->validToken);
WebGUI::Inbox::Message->create(
$session, {
groupId => $f->get('gid'),
subject => $f->get('subject'),
status => 'unread',
message => $f->process('message', 'HTMLArea'),
sentBy => $session->user->userId,
overridePerUserDelivery => $f->get('override'),
extraHeaders => { from => $f->get('from') }
}
);
my $i18n = WebGUI::International->new($session);
return _submenu($session,$i18n->get(812));
}

View file

@ -26,12 +26,15 @@ TODO
=head2 www_ssoViaSessionId
TODO: DOCUMENT ME
Allows a user to login as another user, by referencing that user's sessionId. Requires that
sessionId is passed as a form or URL parameter. It does NOT duplicate the original user's session,
it just switches you to that user.
=cut
sub www_ssoViaSessionId {
my $session = shift;
return undef unless $session->config->get('enableSimpleSSO');
my $sessionId = $session->form->get("sessionId");
if (defined $sessionId && $sessionId ne "") {
if ($sessionId eq $session->getId) {

View file

@ -254,6 +254,7 @@ sub formProperties {
$properties{ options } = $orderedValues;
$properties{ forceImageOnly } = $self->get("forceImageOnly");
$properties{ dataDefault } = $self->get("dataDefault");
$properties{ extras } = $self->get("extras");
return \%properties;
}
@ -335,6 +336,11 @@ sub formField {
if(!defined $properties->{value}) {
$properties->{value} = WebGUI::Operation::Shared::secureEval($session,$properties->{dataDefault});
}
if ($self->getId eq "language") {
if ($self->session->scratch->getLanguageOverride) {
$properties->{value} = $self->session->scratch->getLanguageOverride;
}
}
}
my $form = WebGUI::Form::DynamicField->new($session,%{$properties});
return $form if $returnObject;

View file

@ -0,0 +1,124 @@
package WebGUI::Role::Asset::Dashlet;
use Moose::Role;
=head1 NAME
WebGUI::AssetAspect::Dashlet - Implement features to turn Assets into Dashlets
=head1 SYNOPSIS
This Aspect provides methods that allow a Dashboard to determine, store and retrieve
customization options for Assets.
=head1 DESCRIPTION
=head1 METHODS
#----------------------------------------------------------------------------
=head2 fetchUserOverrides ($dashboardAssetId, [$userId])
Retrieve user preferences for a particular dashboard and user for this Asset from the database.
=head3 $dashboardId
The assetId of the dashboard to reference.
=head3 $userId
The userId to whose preferences should be returned. Uses the current session user if omitted.
=cut
sub fetchUserOverrides {
my $self = shift;
my $dashboardAssetId = shift;
my $userId = shift || $self->session->user->userId;
my $properties_json = $self->session->db->quickScalar('select properties from Dashboard_userPrefs where dashboardAssetId=? and userId=? and dashletAssetId=?',[$dashboardAssetId, $userId, $self->getId,]);
$properties_json ||= '{}';
my $properties = from_json($properties_json);
return $properties;
}
#----------------------------------------------------------------------------
=head2 getOverrideFormDefinition
Return an array ref of form properties. The form properties are those that the
Asset has marked as being able to be overridden by a Dashboard asset by giving
the property the dashletOverridable flag.
Assets that want to allow additional properties outside of their definition should
override and extend this method.
=cut
sub getOverrideFormDefinition {
my $self = shift;
my $session = $self->session;
my @properties;
foreach my $property ( $self->getProperties ) {
my $fieldHash = $self->getFieldData( $property );
next unless $fieldHash->{dashletOverridable};
$fieldHash->{name} = $property;
push @properties, $fieldHash;
}
return @properties;
}
#----------------------------------------------------------------------------
=head2 getUserOverrides
Store user preferences for this Asset. This is direct reference from inside the object, so
if you plan to modify the data, Clone it first.
=cut
sub getUserOverrides {
return shift->{_userOverrides};
}
#----------------------------------------------------------------------------
=head2 setUserOverrides
Store user preferences for this Asset.
=cut
sub setUserOverrides {
shift->{_userOverrides} = shift;
}
#----------------------------------------------------------------------------
=head2 storeUserOverrides ($dashboardAssetId, $properties, [$userId])
Store user preferences for a particular dashboard and user for this Asset to the database.
=head3 $dashboardId
The assetId of the dashboard to reference.
=head3 $userId
The userId to whose preferences should be returned. Uses the current session user if omitted.
=cut
sub storeUserOverrides {
my $self = shift;
my $session = $self->session;
my $dashboardAssetId = shift;
my $properties = shift;
my $userId = shift || $session->user->userId;
my $properties_json = to_json($properties);
$session->db->write('DELETE FROM Dashboard_userPrefs where dashboardAssetId=? and userId=? and dashletAssetId=?',[$dashboardAssetId, $userId, $self->getId]);
$session->db->write('INSERT INTO Dashboard_userPrefs (dashboardAssetId, userId, dashletAssetId, properties) VALUES (?,?,?,?)', [$dashboardAssetId, $userId, $self->getId, $properties_json]);
}
1; # You can't handle the truth

View file

@ -76,14 +76,15 @@ override duplicate => sub {
=head2 addRevision ( properties [, revisionDate, options ] )
Extend addRevision to set skipNotification to 0 for each new revision.
Override addRevision to set skipNotification to 0 for each new revision. This preserves whether or
not a notification was sent for the previous revision.
=cut
around addRevision => sub {
my $orig = shift;
my $self = shift;
my $properties = shift;
my $properties = shift || {};
$properties->{ skipNotification } = 0;

View file

@ -285,6 +285,9 @@ A hash reference containing rules for a search. The rules will will be hash refe
{ keywords => "something to search for", lineage => [ "000001000005", "000001000074000003" ] };
All rules, except for assetIds, are logically AND'ed together to create a finer search. assetIds are OR'ed to the final
query.
=head4 keywords
This rule limits the search results to assets that match keyword criteria.
@ -303,6 +306,8 @@ This rule limits the search to a specific set of assetIds. An array reference of
assetIds => [ "PBasset000000000000001", ]
Unlike every other rule, this rule is logically OR'ed with the other rules.
=head4 classes
This rule limits the search to a specific set of asset classes. An array reference of class names.

View file

@ -35,12 +35,12 @@ These methods are available from this package:
=cut
#-------------------------------------------------------------------
=head2 addFile ( path )
Use an external filter defined in the config file as searchIndexerPlugins.
Use an external filter defined in the config file as searchIndexerPlugins to pull keywords from a file and
add them to the index.
=head3 path
@ -51,22 +51,11 @@ The path to the filename to index, including the filename.
sub addFile {
my $self = shift;
my $path = shift;
my $filters = $self->session->config->get("searchIndexerPlugins");
my $content;
if ($path =~ m/\.(\w+)$/) {
my $type = lc($1);
if ($filters->{$type}) {
open my $fh, "$filters->{$type} $path |" or return undef; # open pipe to filter
$content = do { local $/; <$fh> }; # slurp file
close $fh;
}
}
return $self->addKeywords($content)
if $content =~ m/\S/; # only index if we fine non-whitespace
return undef;
my $keywords = $self->getKeywordsForFile($path);
return unless $keywords =~ /\S/;
return $self->addKeywords($keywords)
}
#-------------------------------------------------------------------
=head2 addKeywords ( text )
@ -84,8 +73,40 @@ sub addKeywords {
my $text = join(" ", @_);
$text = $self->_filterKeywords($text);
my ($keywords) = $self->session->db->quickArray("select keywords from assetIndex where assetId=?",[$self->getId]);
$self->session->db->write("update assetIndex set keywords =? where assetId=?", [$keywords.' '.$text, $self->getId]);
my ($keywords) = $self->session->db->quickArray("select keywords from assetIndex where assetId=? and url=?",[$self->getId, $self->asset->get('url')]);
$self->session->db->write("update assetIndex set keywords =? where assetId=? and url=?", [$keywords.' '.$text, $self->getId, $self->asset->get('url')]);
}
#-------------------------------------------------------------------
=head2 addRecord ( %fields )
Adds a duplicate record for the current asset, along with fields that are overridden.
=head3 %fields
A hash of fields to override in the record. Entries for url and keywords are mandatory, and
no record will be added unless they exist in the hash.
The lineage entry cannot be overridden.
=cut
sub addRecord {
my $self = shift;
my %fields = @_;
return unless $fields{url} and $fields{keywords};
my $asset = $self->asset;
##Get the asset's record from the database.
my %defaults = $self->session->db->quickHash('select * from assetIndex where assetId=? and url=?', [$asset->get('assetId'), $asset->get('url')]);
$fields{keywords} = $self->_filterKeywords($fields{keywords});
%fields = (%defaults, %fields);
$fields{assetId} = $asset->getId;
$fields{lineage} = $defaults{lineage};
my $add = $self->session->db->prepare("replace into assetIndex (assetId, url, title, creationDate, revisionDate,
ownerUserId, groupIdView, groupIdEdit, lineage, className, synopsis, keywords, subId) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )");
$add->execute([@fields{qw/assetId url title creationDate revisionDate ownerUserId groupIdView groupIdEdit lineage className synopsis keywords subId/}]);
}
@ -201,6 +222,36 @@ sub getId {
return $self->{_id};
}
#-------------------------------------------------------------------
=head2 getKeywordsForFile ( path )
Use an external filter defined in the config file as searchIndexerPlugins to get keywords
from a file.
=head3 path
The path to the filename to index, including the filename.
=cut
sub getKeywordsForFile {
my $self = shift;
my $path = shift;
my $filters = $self->session->config->get("searchIndexerPlugins");
my $content;
if ($path =~ m/\.(\w+)$/) {
my $type = lc($1);
if ($filters->{$type}) {
open my $fh, "$filters->{$type} $path |" or return undef; # open pipe to filter
$content = do { local $/; <$fh> }; # slurp file
close $fh;
}
}
return $content;
}
#-------------------------------------------------------------------
=head2 setIsPublic ( boolean )

View file

@ -183,10 +183,7 @@ the one in this user's current session.
sub validToken {
my ($self) = @_;
my $session = $self->session;
$session->log->info('HTTP method: '. $session->request->method);
$session->log->info('CSRF token: '. $session->scratch->get('webguiCsrfToken'));
return 0 unless $session->request->method eq 'POST';
$session->log->info('Web token: '. $self->param('webguiCsrfToken'));
return 0 unless $self->param('webguiCsrfToken') eq $session->scratch->get('webguiCsrfToken');
return 1;
}

View file

@ -97,8 +97,8 @@ sub copy {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'copy.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Copy').'" title="'.$i18n->get('Copy').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'copy.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Copy').'" title="'.$i18n->get('Copy').'" /></a></span>';
return $output;
}
@ -124,8 +124,8 @@ sub cut {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'cut.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Cut').'" title="'.$i18n->get('Cut').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'cut.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Cut').'" title="'.$i18n->get('Cut').'" /></a></span>';
return $output;
}
@ -160,8 +160,8 @@ sub delete {
$confirmText = qq| onclick="return confirm('$confirmText');" |;
}
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'" '.$confirmText.'>';
$output .= '<img src="'.$self->getBaseURL().'delete.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Delete').'" title="'.$i18n->get('Delete').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'" '.$confirmText.'>';
$output .= '<img src="'.$self->getBaseURL().'delete.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Delete').'" title="'.$i18n->get('Delete').'" /></a></span>';
return $output;
}
@ -181,7 +181,7 @@ sub drag {
my $self = shift;
my $extras = shift;
my $i18n = WebGUI::International->new($self->session,'Icon');
return '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><img '.$extras.' src="'.$self->getBaseURL().'drag.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Drag').'" title="'.$i18n->get('Drag').'" /></p>';
return '<span class="toolbarIcon" style="vertical-align:middle;"><img '.$extras.' src="'.$self->getBaseURL().'drag.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Drag').'" title="'.$i18n->get('Drag').'" /></span>';
}
#-------------------------------------------------------------------
@ -206,8 +206,8 @@ sub edit {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'edit.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Edit').'" title="'.$i18n->get('Edit').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'edit.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Edit').'" title="'.$i18n->get('Edit').'" /></a></span>';
return $output;
}
@ -234,8 +234,8 @@ sub export {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'export.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Export').'" title="'.$i18n->get('Export').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'export.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Export').'" title="'.$i18n->get('Export').'" /></a></span>';
return $output;
}
@ -287,8 +287,8 @@ sub locked {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'locked.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('locked').'" title="'.$i18n->get('locked').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'locked.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('locked').'" title="'.$i18n->get('locked').'" /></a></span>';
return $output;
}
@ -315,8 +315,8 @@ sub manage {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'manage.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Manage').'" title="'.$i18n->get('Manage').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'manage.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Manage').'" title="'.$i18n->get('Manage').'" /></a></span>';
return $output;
}
@ -343,8 +343,8 @@ sub moveBottom {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveBottom.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move To Bottom').'" title="'.$i18n->get('Move To Bottom').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveBottom.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move To Bottom').'" title="'.$i18n->get('Move To Bottom').'" /></a></span>';
return $output;
}
@ -376,11 +376,11 @@ sub moveDown {
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $disabled = shift;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;">';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;">';
$output .= '<a href="'.$self->session->url->gateway($pageURL,$urlParams).'">' unless $disabled;
$output .= '<img src="'.$self->getBaseURL().'moveDown.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move Down').'" title="'.$i18n->get('Move Down').'" />';
$output .= '</a>' unless $disabled;
$output .= '</p>';
$output .= '</span>';
return $output;
}
@ -407,8 +407,8 @@ sub moveLeft {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveLeft.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move Left').'" title="'.$i18n->get('Move Left').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveLeft.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move Left').'" title="'.$i18n->get('Move Left').'" /></a></span>';
return $output;
}
@ -435,8 +435,8 @@ sub moveRight {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveRight.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move Right').'" title="'.$i18n->get('Move Right').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveRight.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move Right').'" title="'.$i18n->get('Move Right').'" /></a></span>';
return $output;
}
@ -463,8 +463,8 @@ sub moveTop {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveTop.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move To Top').'" title="'.$i18n->get('Move To Top').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'moveTop.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move To Top').'" title="'.$i18n->get('Move To Top').'" /></a></span>';
return $output;
}
@ -496,11 +496,11 @@ sub moveUp {
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $disabled = shift;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;">';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;">';
$output .= '<a href="'.$self->session->url->gateway($pageURL,$urlParams).'">' unless $disabled;
$output .= '<img src="'.$self->getBaseURL().'moveUp.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Move Up').'" title="'.$i18n->get('Move Up').'" />';
$output .= '</a>' unless $disabled;
$output .= '</p>';
$output .= '</span>';
return $output;
}
@ -563,8 +563,8 @@ sub shortcut {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'shortcut.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Create Shortcut').'" title="'.$i18n->get('Create Shortcut').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'shortcut.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('Create Shortcut').'" title="'.$i18n->get('Create Shortcut').'" /></a></span>';
return $output;
}
@ -591,8 +591,8 @@ sub view {
my $urlParams = shift;
my $pageURL = shift || $self->session->url->getRequestedUrl;
my $i18n = WebGUI::International->new($self->session,'Icon');
my $output = '<p class="toolbarIcon" style="display:inline;vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'view.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('View').'" title="'.$i18n->get('View').'" /></a></p>';
my $output = '<span class="toolbarIcon" style="vertical-align:middle;"><a href="'.$self->session->url->gateway($pageURL,$urlParams).'">';
$output .= '<img src="'.$self->getBaseURL().'view.gif" style="vertical-align:middle;border: 0px;" alt="'.$i18n->get('View').'" title="'.$i18n->get('View').'" /></a></span>';
return $output;
}

View file

@ -77,7 +77,7 @@ sub audit {
#-------------------------------------------------------------------
=head2 canShowPerformanceIndicators ( )
=head2 performanceLogger ( )
Returns true if the user meets the conditions to see performance indicators and performance indicators are enabled.

View file

@ -389,7 +389,7 @@ sub makeAbsolute {
=head2 makeCompliant ( string )
Returns a string that has made into a WebGUI compliant URL based upon the language being submitted.
Returns a string that has been made into a WebGUI compliant URL.
=head3 string

View file

@ -936,8 +936,10 @@ sub www_ajaxPrices {
} || 0,
shipping => eval {
die unless $shipping;
$self->update({ shippingAddressId => $shipping });
#die unless $shipping;
if ( $shipping ) {
$self->update({ shippingAddressId => $shipping });
}
my $ship = WebGUI::Shop::Ship->new($self->session);
$ship->getOptions($self);
} || [],
@ -1234,7 +1236,13 @@ sub www_view {
my $billingAddressId = $self->billingAddressId;
if ($billingAddressId) {
$billingAddressOptions{'update_address'} = sprintf $i18n->get('Update %s'), $self->getBillingAddress->get('label');
my $billingAddress = eval { $self->getBillingAddress };
if ( defined $billingAddress ) {
$billingAddressOptions{'update_address'} = sprintf $i18n->get('Update %s'), $billingAddress->get('label');
}
elsif (my $e = WebGUI::Error->caught("WebGUI::Error::ObjectNotFound") && $self->get('billingAddressId')) {
$self->update({billingAddressId=>''});
}
}
%billingAddressOptions = (%billingAddressOptions, %addressOptions);
@ -1250,7 +1258,10 @@ sub www_view {
my $shippingAddressId = $self->shippingAddressId;
if ($shippingAddressId) {
$shippingAddressOptions{'update_address'} = sprintf $i18n->get('Update %s'), $self->getShippingAddress->get('label');
my $shippingAddress = eval { $self->getShippingAddress };
if ( defined $shippingAddress ) {
$shippingAddressOptions{'update_address'} = sprintf $i18n->get('Update %s'), $shippingAddress->get('label');
}
}
%shippingAddressOptions = (%shippingAddressOptions, %addressOptions);

View file

@ -271,8 +271,9 @@ sub processPayment {
at => $self->identityToken,
);
my $response = LWP::UserAgent->new->post($self->payPalUrl, \%form);
my ($status, @lines) = split("\n", uri_unescape($response->content));
my %params = map { split /=/ } @lines;
my ($status, @lines) = split("\n", $response->content);
my %params = map { split /=/ }
map { uri_unescape($_) } @lines;
if ($status =~ /FAIL/) {
my $message = '<table><tr><th>Field</th><th>Value</th></tr>';

View file

@ -93,7 +93,7 @@ Base class for all modules which do tax calculations in the Shop.
=head1 SYNOPSIS
use base WebGUI::Shop::TaxDriver;
use base 'WebGUI::Shop::TaxDriver';
my $driver = WebGUI::Shop::TaxDriver->new($session);

View file

@ -42,6 +42,11 @@ property shippingAddressName => (
noFormPost => 1,
default => '',
);
property shippingOrganization => (
is => 'rw',
noFormPost => 1,
default => '',
);
property shippingAddress1 => (
is => 'rw',
noFormPost => 1,
@ -122,6 +127,11 @@ property isRecurring => (
noFormPost => 1,
default => '',
);
property paymentOrganization => (
is => 'rw',
noFormPost => 1,
default => '',
);
property paymentAddress1 => (
is => 'rw',
noFormPost => 1,
@ -663,7 +673,9 @@ A hash reference with the address properties.
sub formatAddress {
my ($self, $address) = @_;
my $formatted = $address->{name} . "<br />" . $address->{address1} . "<br />";
my $formatted = $address->{name} . "<br />";
$formatted .= $address->{organization} . "<br />" if ($address->{organization} ne "");
$formatted .= $address->{address1} . "<br />";
$formatted .= $address->{address2} . "<br />" if ($address->{address2} ne "");
$formatted .= $address->{address3} . "<br />" if ($address->{address3} ne "");
$formatted .= $address->{city} . ", ";
@ -797,6 +809,7 @@ sub getTransactionVars {
shippingPrice => sprintf( "%.2f", $self->shippingPrice ),
shippingAddress => $self->formatAddress( {
name => $self->shippingAddressName,
organization => $self->shippingOrganization,
address1 => $self->shippingAddress1,
address2 => $self->shippingAddress2,
address3 => $self->shippingAddress3,
@ -808,6 +821,7 @@ sub getTransactionVars {
} ),
paymentAddress => $self->formatAddress({
name => $self->paymentAddressName,
organization => $self->paymentOrganization,
address1 => $self->paymentAddress1,
address2 => $self->paymentAddress2,
address3 => $self->paymentAddress3,
@ -825,25 +839,31 @@ sub getTransactionVars {
my $address = '';
if ($self->shippingAddressId ne $item->get('shippingAddressId')) {
$address = $self->formatAddress({
name => $item->get('shippingAddressName'),
address1 => $item->get('shippingAddress1'),
address2 => $item->get('shippingAddress2'),
address3 => $item->get('shippingAddress3'),
city => $item->get('shippingCity'),
state => $item->get('shippingState'),
code => $item->get('shippingCode'),
country => $item->get('shippingCountry'),
phoneNumber => $item->get('shippingPhoneNumber'),
name => $item->get('shippingAddressName'),
organization => $self->get('shippingOrganization'),
address1 => $item->get('shippingAddress1'),
address2 => $item->get('shippingAddress2'),
address3 => $item->get('shippingAddress3'),
city => $item->get('shippingCity'),
state => $item->get('shippingState'),
code => $item->get('shippingCode'),
country => $item->get('shippingCountry'),
phoneNumber => $item->get('shippingPhoneNumber'),
});
}
# Post purchase actions
my $actionsLoop = [];
my $actions = $item->getSku->getPostPurchaseActions( $item );
for my $label ( keys %{$actions} ) {
push @{$actionsLoop}, {
label => $label,
url => $actions->{$label},
my $sku = eval { $item->getSku };
my $has_sku = 0;
if (! WebGUI::Error->caught) {
my $actions = $sku->getPostPurchaseActions( $item );
$has_sku = 1;
for my $label ( keys %{$actions} ) {
push @{$actionsLoop}, {
label => $label,
url => $actions->{$label},
}
}
}
@ -861,7 +881,8 @@ sub getTransactionVars {
%{$item->get},
%taxVars,
viewItemUrl => $url->page('shop=transaction;method=viewItem;transactionId='.$self->getId.';itemId='.$item->getId, 1),
price => sprintf("%.2f", $item->get('price')),
hasSku => $has_sku,
price => sprintf( "%.2f", $item->get('price') ),
pricePlusTax => sprintf( "%.2f", $price + $taxAmount ),
extendedPrice => sprintf( "%.2f", $quantity * $price ),
extendedPricePlusTax => sprintf( "%.2f", $quantity * ( $price + $taxAmount ) ),
@ -1222,8 +1243,8 @@ Refunds a specific item from a transaction and then issues shop credit.
sub www_refundItem {
my ($class, $session) = @_;
return $session->privilege->insufficient unless (WebGUI::Shop::Admin->new($session)->canManage);
my $self = $class->new($session, $session->form->get("transactionId"));
my $form = $session->form;
my $self = $class->new($session, $form->get("transactionId"));
my $item = eval { $self->getItem($form->get("itemId")) };
if (WebGUI::Error->caught()) {
$session->log->error("Can't get item ".$form->get("itemId"));
@ -1327,15 +1348,16 @@ sub www_view {
</tr>
<tr>
<th>}. $i18n->get("shipping address") .q{</th><td>}. $transaction->formatAddress({
name => $transaction->get('shippingAddressName'),
address1 => $transaction->get('shippingAddress1'),
address2 => $transaction->get('shippingAddress2'),
address3 => $transaction->get('shippingAddress3'),
city => $transaction->get('shippingCity'),
state => $transaction->get('shippingState'),
code => $transaction->get('shippingCode'),
country => $transaction->get('shippingCountry'),
phoneNumber => $transaction->get('shippingPhoneNumber'),
name => $transaction->get('shippingAddressName'),
organization => $transaction->get('shippingOrganization'),
address1 => $transaction->get('shippingAddress1'),
address2 => $transaction->get('shippingAddress2'),
address3 => $transaction->get('shippingAddress3'),
city => $transaction->get('shippingCity'),
state => $transaction->get('shippingState'),
code => $transaction->get('shippingCode'),
country => $transaction->get('shippingCountry'),
phoneNumber => $transaction->get('shippingPhoneNumber'),
}) .q{</td>
</tr>
<tr>
@ -1346,15 +1368,16 @@ sub www_view {
</tr>
<tr>
<th>}. $i18n->get("payment address") .q{</th><td>}. $transaction->formatAddress({
name => $transaction->get('paymentAddressName'),
address1 => $transaction->get('paymentAddress1'),
address2 => $transaction->get('paymentAddress2'),
address3 => $transaction->get('paymentAddress3'),
city => $transaction->get('paymentCity'),
state => $transaction->get('paymentState'),
code => $transaction->get('paymentCode'),
country => $transaction->get('paymentCountry'),
phoneNumber => $transaction->get('paymentPhoneNumber'),
name => $transaction->get('paymentAddressName'),
organization => $transaction->get('paymentOrganization'),
address1 => $transaction->get('paymentAddress1'),
address2 => $transaction->get('paymentAddress2'),
address3 => $transaction->get('paymentAddress3'),
city => $transaction->get('paymentCity'),
state => $transaction->get('paymentState'),
code => $transaction->get('paymentCode'),
country => $transaction->get('paymentCountry'),
phoneNumber => $transaction->get('paymentPhoneNumber'),
}) .q{</td>
</tr>
</table>
@ -1379,6 +1402,8 @@ sub www_view {
<tbody>
};
foreach my $item (@{$transaction->getItems}) {
eval { $item->getSku; };
my $sku_exists = !WebGUI::Error->caught;
$output .= WebGUI::Form::formHeader($session)
.WebGUI::Form::hidden($session, {name=>"shop",value=>"transaction"})
.WebGUI::Form::hidden($session, {name=>"method",value=>"updateItem"})
@ -1386,9 +1411,15 @@ sub www_view {
.WebGUI::Form::hidden($session, {name=>"itemId",value=>$item->getId})
.q{
<tr>
<td>}.$item->get('lastUpdated').q{</td>
<td><a href="}.$url->page('shop=transaction;method=viewItem;transactionId='.$transaction->getId.';itemId='.$item->getId).q{">}.$item->get('configuredTitle').q{</a></td>
<td>}.$transaction->formatCurrency($item->get('price')).q{</td>
<td>}.$item->get('lastUpdated').qq{</td>\n}.
(
$sku_exists
? q{<td><a href="}.$url->page('shop=transaction;method=viewItem;transactionId='.$transaction->getId
. ';itemId='.$item->getId).q{">}.$item->get('configuredTitle').qq{</a></td>\n}
: q{<td>}.$item->get('configuredTitle').q{<br />}.$i18n->get('item sku deleted').qq{</td>\n}
)
. q{<td>}.$transaction->formatCurrency($item->get('price')).q{</td>
<td>}.$item->get('quantity').q{</td>
};
if ($item->get('shippingAddressId') eq $transaction->get('shippingAddressId')) {
@ -1397,15 +1428,16 @@ sub www_view {
else {
$output .= q{
<td class="smallAddress">}. $transaction->formatAddress({
name => $item->get('shippingAddressName'),
address1 => $item->get('shippingAddress1'),
address2 => $item->get('shippingAddress2'),
address3 => $item->get('shippingAddress3'),
city => $item->get('shippingCity'),
state => $item->get('shippingState'),
code => $item->get('shippingCode'),
country => $item->get('shippingCountry'),
phoneNumber => $item->get('shippingPhoneNumber'),
name => $item->get('shippingAddressName'),
organization => $item->get('shippingOrganization'),
address1 => $item->get('shippingAddress1'),
address2 => $item->get('shippingAddress2'),
address3 => $item->get('shippingAddress3'),
city => $item->get('shippingCity'),
state => $item->get('shippingState'),
code => $item->get('shippingCode'),
country => $item->get('shippingCountry'),
phoneNumber => $item->get('shippingPhoneNumber'),
}) .q{</td>
};
}
@ -1464,7 +1496,12 @@ sub www_viewItem {
$session->log->error("Can't get item ".$session->form->get("itemId"));
return $class->www_view($session);
}
return $item->getSku->www_view;
my $sku = eval { $item->getSku };
if (WebGUI::Error->caught()) {
$session->errorHandler->error("Can't get sku for ".$session->form->get("itemId"));
return $class->www_view($session);
}
return $sku->www_view;
}
#-------------------------------------------------------------------

View file

@ -72,6 +72,11 @@ property shippingName => (
noFormPost => 1,
default => '',
);
property shippingOrganization => (
is => 'rw',
noFormPost => 1,
default => '',
);
property shippingAddress1 => (
is => 'rw',
noFormPost => 1,
@ -356,15 +361,20 @@ sub getSku {
=head2 issueCredit ( )
Returns the money from this item to the user in the form of in-store credit.
Returns the money from this item to the user in the form of in-store credit. Items marked
cancelled cannot be refunded.
=cut
sub issueCredit {
my $self = shift;
return if $self->orderStatus eq 'Cancelled';
return unless $self->transaction->isSuccessful;
my $credit = WebGUI::Shop::Credit->new($self->transaction->session, $self->transaction->userId);
$credit->adjust(($self->price * $self->quantity), "Issued credit on sku ".$self->assetId." for transaction item ".$self->getId." on transaction ".$self->transaction->getId);
$self->getSku->onRefund($self);
if (my $sku = eval {$self->getSku}) {
$sku->onRefund($self);
}
$self->update({orderStatus=>'Cancelled'});
}

View file

@ -44,6 +44,8 @@ This package provides a mechanism for storing and retrieving files that are not
$store = WebGUI::Storage->createTemp($self->session);
$store = WebGUI::Storage->get($self->session,$id);
$exists = WebGUI::Storage->storageExists($session, $id);
$filename = $store->addFileFromFilesystem($pathToFile);
$filename = $store->addFileFromFormPost($formVarName,$attachmentLimit);
$filename = $store->addFileFromHashref($filename,$hashref);
@ -304,7 +306,10 @@ sub addFileFromFilesystem {
if (! defined $pathToFile) {
return undef;
}
##Handle UTF-8 filenames.
$pathToFile = Encode::encode_utf8($pathToFile);
$pathToFile = Cwd::realpath($pathToFile); # trace any symbolic links
$pathToFile = Encode::decode_utf8($pathToFile);
if (-d $pathToFile) {
$self->session->log->error($pathToFile." is a directory, not a file.");
return undef;
@ -370,6 +375,7 @@ sub addFileFromFormPost {
return $filename;
}
my $clientFilename = $upload->filename;
$clientFilename = Encode::decode_utf8($clientFilename);
next
unless $clientFilename;
next
@ -1078,7 +1084,7 @@ sub getFiles {
callback => sub {
my $obj = shift;
my $rel = $obj->relative($dir);
my $str = $rel->stringify;
my $str = Encode::decode_utf8($rel->stringify);
if (! $showAll ) {
return if $str =~ /^thumb-/;
return if $str =~ /^\./;
@ -1704,36 +1710,34 @@ sub setPrivileges {
}
}
my $public;
for my $user (@{ $privs{users} }) {
if ($user eq '1') {
$public = 1;
}
}
for my $group (@{ $privs{groups} }) {
if ($group eq '1' || $group eq '7') {
$public = 1;
}
}
my $accessFile = JSON->new->encode( \%privs );
return $self->writeAccess( %privs );
}
my $dirObj = $self->getPathClassDir();
return undef if ! defined $dirObj;
$dirObj->recurse(
callback => sub {
my $obj = shift;
return unless $obj->is_dir;
my $rel = $obj->relative($dirObj);
if ($public) {
$self->deleteFile($rel->file('.wgaccess')->stringify);
}
else {
$self->addFileFromScalar($rel->file('.wgaccess')->stringify, $accessFile);
}
}
);
#-------------------------------------------------------------------
=head2 storageExists ( $session, $storageId )
Class method to determine if a storage location exists. This can't be done
with C<get> since it will create it if it doesn't exist. Returns true if the
storage directory exists.
=head3 $session
A session object, used to find the uploadsPath location
=head3 $storageId
A WebGUI::Storage GUID.
=cut
sub storageExists {
my ($class, $session, $storageId) = @_;
my $hexId = $session->id->toHex($storageId);
my @parts = ($hexId =~ m/^((.{2})(.{2}).+)/)[1,2,0];
my $dir = Path::Class::Dir->new($session->config->get('uploadsPath'), @parts);
return (-e $dir->stringify && -d _ ? 1 : 0);
}
@ -1808,6 +1812,19 @@ sub tar {
return $temp;
}
#----------------------------------------------------------------------------
=head2 trash ( )
Set this storage location as trashed
=cut
sub trash {
my ( $self ) = @_;
return $self->writeAccess( state => "trash" );
}
#-------------------------------------------------------------------
=head2 untar ( filename [, storage ] )
@ -1867,5 +1884,64 @@ sub getDirectoryId {
return $self->{_pathParts}[2];
}
#----------------------------------------------------------------------------
=head2 writeAccess( pairs )
Write the given pairs to the wgaccess files in this storage location.
If the storage location should be public, will remove all wgaccess files.
Possible keys:
users - an arrayref of userIds to allow access to
groups - an arrayref of groupIds to allow access to
assets - an arrayref of assetIds to validate permissions against
state - a string describing a special state.
valid values: "trash" - storage location is trashed
=cut
sub writeAccess {
my ( $self, %privs ) = @_;
my $public;
if ( $privs{users} ) {
for my $user (@{ $privs{users} }) {
if ($user eq '1') {
$public = 1;
}
}
}
if ( $privs{groups} ) {
for my $group (@{ $privs{groups} }) {
if ($group eq '1' || $group eq '7') {
$public = 1;
}
}
}
my $accessFile = JSON->new->encode( \%privs );
my $dirObj = $self->getPathClassDir();
return undef if ! defined $dirObj;
$dirObj->recurse(
callback => sub {
my $obj = shift;
return unless $obj->is_dir;
my $rel = $obj->relative($dirObj);
if ($public) {
$self->deleteFile($rel->file('.wgaccess')->stringify);
}
else {
$self->addFileFromScalar($rel->file('.wgaccess')->stringify, $accessFile);
}
}
);
return;
}
1;

View file

@ -133,6 +133,7 @@ sub _initSession {
'Payment Drivers' => 'paymentGateway',
'Database Links' => 'databaseLink',
'LDAP Links' => 'ldapLink',
'Profile Fields' => 'userProfileField',
);
my %initCounts;
for ( my $i = 0; $i < @checkCount; $i += 2) {
@ -640,6 +641,7 @@ were passed in. Currently able to destroy:
WebGUI::LDAPLink
WebGUI::Inbox::Message
WebGUI::Fork
WebGUI::ProfileField
Example call:
@ -723,6 +725,7 @@ Example call:
pbworkflow000000000006
pbworkflow000000000007
send_webgui_statistics
xR-_GRRbjBojgLsFx3dEMA
};
},
);
@ -743,6 +746,7 @@ Example call:
'WebGUI::Inbox::Message' => 'purge',
'WebGUI::AdSpace' => 'delete',
'WebGUI::FilePump::Bundle' => 'delete',
'WebGUI::ProfileField' => 'delete',
'WebGUI::Shop::Cart' => sub {
my $cart = shift;
my $addressBook = eval { $cart->getAddressBook(); };

View file

@ -21,6 +21,7 @@ use JSON ();
use WebGUI::Exception;
use WebGUI::ProfileField;
use WebGUI::Inbox;
use List::MoreUtils qw( any );
use Scalar::Util qw( weaken );
use Net::CIDR::Lite;
use WebGUI::Friends;
@ -395,7 +396,6 @@ sub delete {
my $userId = $self->userId;
my $session = $self->session;
my $db = $session->db;
$self->uncache;
foreach my $groupId ( @{ $self->getGroups } ) {
my $group = WebGUI::Group->new($session, $groupId);
@ -446,6 +446,7 @@ sub delete {
$credit->purge;
# remove user itself
$self->uncache;
$db->write("DELETE FROM userProfileData WHERE userId=?",[$userId]);
$db->write("DELETE FROM users WHERE userId=?",[$userId]);
}
@ -806,7 +807,7 @@ Field to get privacy setting for.
sub getProfileFieldPrivacySetting {
my $self = shift;
my $session = $self->session;
my $field = shift;
my $fieldId = shift;
unless ($self->{_privacySettings}) {
#Look it up manually because we want to cache this separately.
@ -818,12 +819,16 @@ sub getProfileFieldPrivacySetting {
$self->{_privacySettings} = JSON->new->decode($privacySettings);
}
return $self->{_privacySettings} unless ($field);
return $self->{_privacySettings} unless ($fieldId);
#No privacy settings returned the privacy setting field
return "none" if($field eq "privacyFields");
return "none" if($fieldId eq "privacyFields");
return $self->{_privacySettings}->{$field};
if (exists $self->{_privacySettings}->{$fieldId}) {
return $self->{_privacySettings}->{$fieldId};
}
my $field = WebGUI::ProfileField->new($session, $fieldId);
return $field && $field->get('defaultPrivacySetting');
}
@ -919,6 +924,25 @@ sub isAdmin {
#-------------------------------------------------------------------
=head2 isDuplicateEmail( email )
Returns true if the email passed is also being used by any other user
=cut
sub isDuplicateEmail {
my ( $self, $email ) = @_;
my @userIds = $self->session->db->quickArray(
"SELECT userId FROM users WHERE email = ?",
[ $email ],
);
return any { $_ ne $self->userId } @userIds;
}
#-------------------------------------------------------------------
=head2 isEnabled ()
Returns 1 if the user is enabled.
@ -1593,7 +1617,7 @@ sub validateProfileDataFromForm {
push(@{$errorFields},$fieldId);
}
#Duplicate emails throw warnings
elsif($fieldId eq "email" && $field->isDuplicate($fieldValue,$self->userId)) {
elsif($fieldId eq "email" && $self->isDuplicateEmail($fieldValue)) {
$errorCat = $field->get("profileCategoryId") unless (defined $errorCat);
push (@{$warnings},$i18n->get(1072));
push(@{$warnFields},$fieldId);

View file

@ -193,9 +193,11 @@ sub commit {
my $now = time;
my $finished = 1;
foreach my $asset (@{$self->getAssets({"byLineage"=>1, onlyPending=>1})}) {
$self->session->log->info( "Committing " . $asset->getId );
$asset->commit;
if ($now + $timeout < time) {
$finished = 0;
$self->session->log->info( "NOES!" );
last;
}
}

View file

@ -551,6 +551,14 @@ sub set {
$self->session->db->setRow("Workflow","workflowId",$self->{_data});
}
=head1 SEE ALSO
L<WebGUI::Workflow::Instance>
After creating a workflow, you need to create an instance of that workflow to kick off activities in it.
=cut
1;

View file

@ -23,6 +23,7 @@ use WebGUI::Asset::Event;
use WebGUI::DateTime;
use DateTime::TimeZone;
use Data::Dumper;
use Data::ICal;
use LWP::UserAgent;
use JSON ();
@ -124,10 +125,6 @@ sub execute {
$session->db->write("REPLACE INTO userSessionScratch (sessionId,name,value) VALUES (?,?,?)",
[$session->getId,$calendar->getId,"SPECTRE"]);
}
#/KLUDGE
## Somebody point me to a DECENT iCalendar parser...
# Text::vFile perhaps?
# Get the feed
$session->log->info( "Trying Calendar feed ".$feed->{url}." for $calendarTitle" );
@ -142,88 +139,52 @@ sub execute {
next FEED;
}
my $data = $response->content;
# If doesn't start with BEGIN:VCALENDAR then error
unless ($data =~ /^BEGIN:VCALENDAR/i) {
my $data = $response->content;
my $cal = Data::ICal->new( data => $data );
if (!$cal) {
# Update the result and last updated fields
$feed->{lastResult} = "Not an iCalendar feed";
$feed->{lastResult} = "Error parsing iCal feed";
$feed->{lastUpdated} = $dt;
$calendar->setFeed($feed->{feedId}, $feed);
next FEED;
#next FEED;
}
my $active = 0; # Parser on/off
my %current_event = ();
my %events;
my $line_number = 0;
$data =~ s/[ \t]?[\r\n]+[ \t]+/ /msg; #Process line continuations
LINE: for my $line (split /[\r\n]+/,$data) {
chomp $line;
$line_number++;
next unless $line =~ /\w/;
#warn "LINE $line_number: $line\n";
if ($line =~ /^BEGIN:VEVENT$/i) {
$active = 1;
next LINE;
}
elsif ($line =~ /^END:VEVENT$/i) {
$active = 0;
# Flush event
my $uid = lc $current_event{uid}[1];
delete $current_event{uid};
$events{$uid} = {%current_event};
$session->log->info( "Found event $uid from feed " . $feed->{feedId} );
%current_event = ();
next LINE;
}
else {
# Flush old entry
# KEY;ATTRIBUTE=VALUE;ATTRIBUTE=VALUE:KEYVALUE
my ($key_attrs,$value) = split /:/,$line,2;
my @attrs = $key_attrs ? (split /;/, $key_attrs) : ();
my $key = shift @attrs;
my %attrs;
while (my $attribute = shift @attrs) {
my ($attr_key, $attr_value) = split /=/, $attribute, 2;
$attrs{lc $attr_key} = $attr_value;
}
$current_event{lc $key} = [\%attrs,$value];
}
}
my $feedData = $feedList->{$feed->{feedId}} = {
added => 0,
updated => 0,
errored => 0,
assetId => $calendar->getId,
};
EVENT: for my $id (keys %events) {
EVENT: foreach my $entry (@{ $cal->entries }) {
next EVENT unless $entry->ical_entry_type eq 'VEVENT';
#use Data::Dumper;
#warn "EVENT: $id; ".Dumper $events{$id};
my $event_properties = $entry->properties;
# Prepare event data
my $properties = {
feedUid => $id,
feedId => $feed->{feedId},
description => _unwrapIcalText($events{$id}->{description}->[1]),
title => _unwrapIcalText($events{$id}->{summary}->[1]),
location => _unwrapIcalText($events{$id}->{location}->[1]),
menuTitle => substr($events{$id}->{summary}->[1],0,15),
className => 'WebGUI::Asset::Event',
isHidden => 1,
};
PROPERTY: foreach my $property (qw/uid description summary location/) {
next PROPERTY unless exists $event_properties->{$property};
$properties->{$property} = $event_properties->{$property}->[0]->value;
}
##Fixup
$properties->{title} = delete $properties->{summary};
$properties->{feedUid} = delete $properties->{uid};
# Prepare the date
my $dtstart = $events{$id}->{dtstart}->[1];
my $dtstart = $event_properties->{dtstart}->[0]->value;
if ($dtstart =~ /T/) {
my ($date, $time) = split /T/, $dtstart;
my ($year, $month, $day) = $date =~ /(\d{4})(\d{2})(\d{2})/;
my ($hour, $minute, $second) = $time =~ /(\d{2})(\d{2})(\d{2})/;
my $tz = $events{$id}->{dtstart}->[0]->{tzid};
my $tz = '';
if ($event_properties->{dtstart}->[0]->{tzid}) {
$tz = $event_properties->{dtstart}->[0]->{tzid};
}
if (!$tz || !DateTime::TimeZone->is_valid_name($tz)) {
$tz = "UTC";
}
@ -253,14 +214,14 @@ sub execute {
next EVENT;
}
my $dtend = $events{$id}->{dtend}->[1];
my $duration = $events{$id}->{duration}->[1];
my $dtend = exists $event_properties->{dtend} ? $event_properties->{dtend}->[0]->value : undef;
my $duration = exists $event_properties->{duration} ? $event_properties->{duration}->[0]->value : undef;
if ($dtend =~ /T/) {
my ($date, $time) = split /T/, $dtend;
my ($year, $month, $day) = $date =~ /(\d{4})(\d{2})(\d{2})/;
my ($hour, $minute, $second) = $time =~ /(\d{2})(\d{2})(\d{2})/;
my $tz = $events{$id}->{dtend}->[0]->{tzid};
my $tz = '';
if (!$tz || !DateTime::TimeZone->is_valid_name($tz)) {
$tz = "UTC";
}
@ -330,28 +291,15 @@ sub execute {
}
# If there are X-WebGUI-* fields
for my $key (grep /^x-webgui-/, keys %{$events{$id}}) {
my $property_name = $key;
$property_name =~ s/^x-webgui-//;
$property_name = lc $property_name;
if ($property_name eq "groupidedit") {
$properties->{groupIdEdit} = $events{$id}->{$key}->[1];
}
elsif ($property_name eq "groupidview") {
$properties->{groupIdView} = $events{$id}->{$key}->[1];
}
elsif ($property_name eq "url") {
$properties->{url} = $events{$id}->{$key}->[1];
}
elsif ($property_name eq "menutitle") {
$properties->{menuTitle} = $events{$id}->{$key}->[1];
}
PROPERTY: foreach my $key (qw/groupIdEdit groupIdView url menuTitle timeZone/) {
my $property_name = 'x-webgui-'.lc $key;
next PROPERTY unless exists $event_properties->{$property_name};
$properties->{$key} = $event_properties->{$property_name}->[0]->value;
}
my $recur;
if ($events{$id}->{rrule}) {
$recur = _icalToRecur($session, $properties->{startDate}, $events{$id}->{rrule}->[1]);
if (exists $event_properties->{rrule}) {
$recur = _icalToRecur($session, $properties->{startDate}, $event_properties->{rrule}->[0]->value);
}
# save events for later

View file

@ -19,6 +19,7 @@ use strict;
use base 'WebGUI::Workflow::Activity';
use WebGUI::International;
use WebGUI::Asset;
use WebGUI::VersionTag;
use DateTime;
=head1 NAME
@ -74,8 +75,8 @@ sub execute {
return $self->COMPLETE unless $piped;
my ( $recurId, $rest ) = split /\|/, $piped, 2;
$self->processRecurrence( $recurId, $timeLimit )
and $piped = $rest;
$self->processRecurrence( $recurId, $timeLimit );
$piped = $rest;
}
$instance->setScratch( recurrences => $piped );
@ -165,20 +166,36 @@ sub processRecurrence {
my ( $self, $recurId, $timeLimit ) = @_;
my $eventId = $self->findLastEventId($recurId);
my $event = WebGUI::Asset::Event->newById( $self->session, $eventId );
if (! $event) {
$self->session->log->warn("Unable to instanciate event with assetId $eventId");
return 0;
}
##Ignore assets in the trash. Same with assets in the clipboard since they would not be
##put into the clipboard.
if ($event->state ne 'published') {
return 0;
}
my $recur = $event->getRecurrence;
my $versionTag = WebGUI::VersionTag->create($self->session, {name => 'Extend Calendar Recurrence for assetId '.$event->getId, });
$versionTag->setWorking();
my $start = $event->getDateTimeStart->truncate(to => 'day');
my $limit = DateTime->today->add( years => 2 );
my $end = $event->limitedEndDate($limit);
my $set = $event->dateSet( $recur, $start, $end );
my $i = $set->iterator;
while ( my $d = $i->next ) {
return if ( time > $timeLimit );
my $time_limit = 0;
DATE: while ( my $d = $i->next ) {
if ( time > $timeLimit ) {
$time_limit = 1;
last DATE;
}
$event->generateRecurrence($d);
}
return 1;
$versionTag->commit;
return $time_limit ? 1 : 0;
} ## end sub processRecurrence
1;

View file

@ -108,7 +108,7 @@ sub execute {
comments => $versionTag->get('comments'),
url => $urlOfSingleAsset,
};
my $template = WebGUI::Asset->newByDynamicClass($self->session, $self->get('templateId'));
my $template = WebGUI::Asset->newById($self->session, $self->get('templateId'));
my $message = $template->process($var);
my $properties = {
status=>"completed",

View file

@ -295,7 +295,7 @@ sub sendMessage {
comments => $versionTag->get('comments'),
url => $approvalUrl,
};
my $template = WebGUI::Asset->newByDynamicClass($self->session, $self->get('templateId'));
my $template = WebGUI::Asset->newById($self->session, $self->get('templateId'));
my $messageText = $template->process($var);
for my $groupId ( @{ $self->getGroupToApprove } ) {
my $message

View file

@ -99,9 +99,20 @@ sub execute {
$log->info("Found subscription $subscription");
my ($fieldId, $value) = split("~", $subscription);
$log->info("Searching for threads that match $subscription");
my $matchingThreads = $db->read("select metaData_values.assetId from metaData_values
left join asset using (assetId) where fieldId=? and value like ? and creationDate > ?
and className like ? and lineage like ? and state = ?",
my $matchingThreads = $db->read("
select mv.assetId
from metaData_values mv
left join asset a using (assetId)
left join assetData d on
mv.assetId = d.assetId
and mv.revisionDate = d.revisionDate
and d.revisionDate = (
select max(revisionDate)
from assetData d2
where d2.assetId = d.assetId
)
where mv.fieldId=? and mv.value like ? and a.creationDate > ?
and a.className like ? and a.lineage like ? and a.state = ?",
[$fieldId, '%'.$value.'%', $lastTimeSent, 'WebGUI::Asset::Post::Thread%', $newsletter->get("lineage").'%', 'published']);
while (my ($threadId) = $matchingThreads->array) {
next

View file

@ -0,0 +1,100 @@
package WebGUI::Workflow::Activity::UpdateAssetSubscribers;
=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::User;
use WebGUI::Group;
=head1 NAME
Package WebGUI::Workflow::Activity::UpdateCollaborationSubscribers
=head1 DESCRIPTION
This workflow activity should be called whenever permissions to view a Collaboration System
are changed. It will remove users who are no longer able to view the CS.
=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::definition() for details.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, "Workflow_Activity_UpdateAssetSubscribers");
push(@{$definition}, {
name => $i18n->get("name"),
properties => { }
});
return $class->SUPER::definition($session,$definition);
}
#-------------------------------------------------------------------
=head2 execute ( )
See WebGUI::Workflow::Activity::execute() for details.
=cut
sub execute {
my $self = shift;
my $asset = shift;
return unless $asset->get('subscriptionGroupId');
my $expireTime = time() + $self->getTTL();
my $subscriptionGroup = WebGUI::Group->new($self->session, $asset->get('subscriptionGroupId'));
##Deserialize from scratch
my @users = @{ $subscriptionGroup->getUsers }; ##Cache
my @usersToDelete = (); ##Cache
##Note, we could use grep here, but we can't interrupt if the workflow runs too long
USER: foreach my $userId (@users) {
if (time() > $expireTime) {
#return $self->WAITING(1);
}
next USER if $asset->canView($userId);
push @usersToDelete, $userId;
}
if (@usersToDelete) {
$subscriptionGroup->deleteUsers(\@usersToDelete);
}
#Clear scratch
return $self->COMPLETE;
}
1;

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