Add Asset Dashlets to Dashboard. Add required and static properties to Dashboard Assets. Add caching to StockData and WeatherData assets. Add LastModifiedBy macro. Add GroupManager to the Group form control.

This commit is contained in:
Colin Kuskie 2011-03-09 21:33:44 -08:00
parent 79aa44cf7e
commit 88797c1d6c
42 changed files with 3506 additions and 448 deletions

View file

@ -323,13 +323,17 @@ sub purge {
=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'),
@ -363,60 +367,84 @@ sub view {
my @found;
my $newStuff;
my $showPerformance = $self->session->errorHandler->canShowPerformanceIndicators();
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 (isIn($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 (isIn($child->getId, @found)||isIn($child->getId,@hidden)) {
if ($child->canView) {
$child->{_properties}{title} = $child->getShortcut->get("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->getPrefsFieldToShow) ? $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.'\';';
}
@ -425,9 +453,9 @@ sub view {
$vars{showAdmin} = ($self->session->var->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);
@ -435,6 +463,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->newByDynamicClass($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->newByDynamicClass($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->newByDynamicClass($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

@ -121,7 +121,7 @@ Overridden to check the revision dates of children as well
sub getContentLastModified {
my $self = shift;
my $mtime = $self->get("revisionDate");
my $mtime = $self->get("lastModified");
my $childIter = $self->getLineageIterator(["children"]);
while ( 1 ) {
my $child;
@ -139,6 +139,36 @@ sub getContentLastModified {
#-------------------------------------------------------------------
=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.

View file

@ -443,6 +443,36 @@ sub getContentLastModified {
#-------------------------------------------------------------------
=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

@ -16,7 +16,8 @@ use WebGUI::Utility;
use WebGUI::Asset::Wobject;
use Finance::Quote;
our @ISA = qw(WebGUI::Asset::Wobject);
use Class::C3;
use base qw/WebGUI::Asset::Wobject WebGUI::AssetAspect::Dashlet/;
#-------------------------------------------------------------------
@ -165,7 +166,9 @@ sub _convertToEpoch {
if($time =~ m/pm/i) {
$hour += 12;
}
$hour ||= 0;
$hour = $self->_appendZero($hour);
$minute ||= 0;
$minute = $self->_appendZero($minute);
my $epoch = eval {$self->session->datetime->humanToEpoch("$year-$month-$day $hour:$minute:00")};
return $epoch;
@ -184,21 +187,47 @@ 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->getValue("failover")) {
$q->failover(0);
}
# Create a new Finance::Quote object
my $q = Finance::Quote->new;
# Disable failover if specified
unless ($self->getValue("failover")) {
$q->failover(0);
}
# Hardcoded timeout for now.
$q->timeout(15);
# Hardcoded timeout for now.
$q->timeout(15);
my $source = $self->getValue('source');
my %stocks = ();
my @stocks_to_fetch = ();
STOCK: foreach my $stock (@{$stocks}) {
$stock = uc $stock;
my $cache = WebGUI::Cache->new($session, [$self->getId, $source, $stock]);
if ($cache->get()) {
my $value = $cache->get();
%stocks = (%stocks, %{ $value });
}
else {
push @stocks_to_fetch, $stock;
}
}
# Fetch the stock information and return the results
return $q->fetch($self->getValue("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 $cache = WebGUI::Cache->new($session, [$self->getId, $source, $stock]);
my %slice;
@slice{ @stock_keys } = @new_stocks{ @stock_keys };
$slice{$stock,'last_fetch'} = time();
$cache->set(\%slice, $self->get('cacheTimeout'));
%stocks = (%stocks, %slice);
}
return \%stocks;
}
#-------------------------------------------------------------------
@ -284,6 +313,7 @@ sub definition {
tab=>'properties',
label=> $i18n->get("default_stock_label"),
hoverHelp=> $i18n->get("default_stock_label_description"),
dashletOverridable => 1,
},
source=>{
fieldType=>"selectList",
@ -297,8 +327,16 @@ sub definition {
fieldType=>"yesNo",
defaultValue=>undef,
label=> $i18n->get("failover_label"),
hoverHelp=> $i18n->get("failover_description")
}
hoverHelp=> $i18n->get("failover_description"),
},
cacheTimeout => {
tab => "display",
fieldType => "interval",
defaultValue => 3600,
uiLevel => 5,
label => $i18n->get("cache timeout", 'Asset_Snippet'),
hoverHelp => $i18n->get("cache timeout help"),
},
);
push(@{$definition}, {
@ -370,7 +408,8 @@ sub view {
$var->{'stock.display.url'} = $self->getUrl("func=displayStock;symbol=");
#Build list of stocks as an array
my $defaults = $self->getValue("defaultStocks");
my $overrides = $self->fetchUserOverrides($self->getParent->getId);
my $defaults = $overrides->{defaultStocks} || $self->getValue("defaultStocks");
#replace any windows newlines
$defaults =~ s/\r//;
my @array = split("\n",$defaults);

View file

@ -17,7 +17,8 @@ package WebGUI::Asset::Wobject::WeatherData;
use strict;
use Weather::Com::Finder;
use WebGUI::International;
use base 'WebGUI::Asset::Wobject';
use Class::C3;
use base qw/WebGUI::Asset::Wobject WebGUI::AssetAspect::Dashlet/;
use WebGUI::Utility;
#-------------------------------------------------------------------
@ -62,8 +63,17 @@ sub definition {
defaultValue=>"Madison, WI\nToronto, Canada\n53536",
tab=>"properties",
hoverHelp=>$i18n->get("Your list of default weather locations"),
label=>$i18n->get("Default Locations")
label=>$i18n->get("Default Locations"),
dashletOverridable => 1,
},
cacheTimeout => {
tab => "display",
fieldType => "interval",
defaultValue => 3600,
uiLevel => 5,
label => $i18n->get("cache timeout", 'Asset_Snippet'),
hoverHelp => $i18n->get("cache timeout help"),
},
};
push(@{$definition}, {
tableName=>'WeatherData',
@ -110,49 +120,72 @@ 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->get("partnerId") ne "" && $self->get("licenseKey") ne "") {
foreach my $location (split("\n", $self->get("locations"))) {
my $weather = Weather::Com::Finder->new({
'partner_id' => $self->get("partnerId"),
'license' => $self->get("licenseKey"),
'cache' => '/tmp',
});
next unless defined $weather;
my $overrides = $self->fetchUserOverrides($self->getParent->getId);
my $locations = $overrides->{locations} || $self->get('locations');
foreach my $location (split("\n", $locations)) {
my $cache = WebGUI::Cache->new($session, [$self->getId, $location]);
my $loop_data;
my $link_data = [];
my $cached_data = $cache->get();
if ($cached_data) {
$loop_data = $cached_data->{locations};
$link_data = $cached_data->{links} || [];
}
else {
my $weather = Weather::Com::Finder->new({
'partner_id' => $self->get("partnerId"),
'license' => $self->get("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 $cache = WebGUI::Cache->new($session, [$self->getId, $location]);
my $cached_data = {
locations => $loop_data,
links => $link_data,
};
$cache->set($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});