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

@ -1821,6 +1821,19 @@ sub getContentLastModified {
}
#-------------------------------------------------------------------
=head2 getContentLastModifiedBy ( )
Returns the userId that modified the content last.
=cut
sub getContentLastModifiedBy {
my $self = shift;
return $self->get("revisedBy");
}
#-------------------------------------------------------------------
=head2 getValue ( key )

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

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

@ -141,6 +141,31 @@ 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->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->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-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-webgui/build/form/groupManager.css'), { rel => 'stylesheet', type => 'text/css' });
}
#-------------------------------------------------------------------
=head2 isDynamicCompatible ( )
@ -180,9 +205,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 +219,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

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

@ -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,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

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

View file

@ -30,16 +30,6 @@ our $I18N = {
message => q|The group whose users may save their personalizations/preferences to the site. If someone is in the "Who can view?" group but not in this group, they can personalize the arrangement of the Dashlets (whose positions will be saved in cookies), but they will not be able to edit the preferences of any particular Dashlet.|,
lastUpdated => 1133619940
},
'dashboard template field label' => {
message => q|Dashboard Template|,
lastUpdated => 1133619940
},
'dashboard template field label' => {
message => q|Dashboard Template|,
lastUpdated => 1133619940
},
'assets to hide' => {
message => q|Assets To Hide.|,
lastUpdated => 1118942468
@ -57,6 +47,168 @@ checkbox for any Asset that you do not want displayed in the Page Layout Asset.
lastUpdated => 1230356526,
},
'Edit Dashlet' => {
message => q|Edit Dashlet|,
lastUpdated => 1230356526,
context => q|A dashlet is an asset being displayed by the Dashboard. It may not have a translation.|,
},
'Is static' => {
message => q|Is static|,
lastUpdated => 1230356526,
context => q|Can it be moved, or rearranged?|,
},
'Is static help' => {
message => q|Can this dashlet be moved around on the dashboard by users of the Dashboard?|,
lastUpdated => 1230356526,
context => q|Can it be moved, or rearranged?|,
},
'Is required' => {
message => q|Is required|,
lastUpdated => 1230356526,
context => q|Can it be deleted from a dashboard by a user?|,
},
'Is required help' => {
message => q|Can this dashlet be deleted from the dashboard by users of the Dashboard?|,
lastUpdated => 1230356526,
context => q|Can it be moved, or rearranged?|,
},
'Dashboard Template Variables' => {
message => q|Dashboard Template Variables|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'dragger.init' => {
message => q|Javascript necessary to initialize the Dashboard. It should be placed at the bottom of the template.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'fullUrl' => {
message => q|The full URL to this Dashboard, including sitename and any gateway configuration.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'canEdit' => {
message => q|A boolean which will be true if the current user can edit this Dashboard.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'positionN_loop' => {
message => q|By default, there are four positions, numbered 1, 2, 3 and 4. Each loop contains the list of assets that have been placed into it. Position 1 is special, because it also contains any assets which have not been specifically placed by the user.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'id' => {
message => q|Asset ID of the current dashlet.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'content' => {
message => q|The dashlet's content|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'dashletTitle' => {
message => q|The title of the dashlet, the raw asset title.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'shortcutUrl' => {
message => q|If this dashlet is a shortcut, the URL of the shortcut.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'dashletUrl' => {
message => q|The URL to this dashlet.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'canDelete' => {
message => q|A boolean that is true if the current user is in the group who can personalize the dashboard and if this dashlet is not set to be required.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'canMove' => {
message => q|A boolean that is true if the current user is in the group who can personalize the dashboard and if this dashlet is not set to be static.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'canPersonalize' => {
message => q|A boolean that is true if the current user is in the group who can personalize the dashboard.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'showReloadIcon' => {
message => q|A boolean that is true if this dashlet is a shortcut, and the Show Reload Icon property is set to be true.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'canEditUserPrefs' => {
message => q|A boolean that is true if the current user is in the Registered Users group, and the dashlet is a Shortcut, and the Shortcut has preferences that can be configured.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'dashboard asset template variables title' => {
message => q|Dashboard Asset Template Variables|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'templateId' => {
message => q|The GUID of the template used to display the dashboard|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'adminsGroupId' => {
message => q|The GUID of the group that is allowed to set the default appearance of the dashboard for visitors.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'usersGroupId' => {
message => q|The GUID of the group that is allowed to change the appearance of their own dashboard.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'isInitialized' => {
message => q|A boolean which is true if this Dashboard has been initialized. You really don't need to know more than that.|,
lastUpdated => 1230356526,
context => q|Template variable help|,
},
'Add New Content' => {
message => q|Add New Content|,
lastUpdated => 1230356526,
context => q|i18n phrase for the view template|,
},
'editFormUrl' => {
message => q|The URL to fetch the user overrides form for this dashlet, whether it is Shortcut based or a regular asset with overrides.|,
lastUpdated => 1230356526,
context => q|i18n phrase for the view template|,
},
};
1;

View file

@ -514,6 +514,12 @@ our $I18N = {
lastUpdated => 1229493261,
},
'cache timeout help' => {
message => q|How long should lookups for each stock symbol be cached internally? Note, the default template has javascript that does fetches on the client side.|,
context => q|Template variable help|,
lastUpdated => 1229493261,
},
};
1;

View file

@ -102,6 +102,12 @@ our $I18N = {
lastUpdated => 1167972337
},
'cache timeout help' => {
message => q|How long should lookups for each location be cached internally?|,
context => q|Template variable help|,
lastUpdated => 1229493261,
},
};
1;

View file

@ -0,0 +1,33 @@
package WebGUI::i18n::English::Form_Group;
use strict; ##Required for all good Perl::Critic compliant code
our $I18N = {
'Group Manager' => {
message => q|Group Manager|,
lastUpdated => 1131394070, #seconds from the epoch
context => q|A form to add or do minor edits to groups|
},
'Add User...' => {
message => q|Add User...|,
lastUpdated => 1131394070, #seconds from the epoch
context => q|Hint for a text box where you enter in a username|
},
'Add Group...' => {
message => q|Add Group...|,
lastUpdated => 1131394070, #seconds from the epoch
context => q|Hint for a text box where you enter in a groupname|
},
'New Group' => {
message => q|New Group|,
lastUpdated => 1131394070, #seconds from the epoch
context => q|Label for a button to create a new group.|
},
};
1;
#vim:ft=perl

View file

@ -92,6 +92,12 @@ our $I18N = {
lastUpdated => 1096319562
},
'Add' => {
message => q|Add|,
lastUpdated => 1096319562,
context => q|to add something new, to create/acquire|,
},
'Edit help' => {
message => q|Edit the properties of this Asset. This icon is only available if the asset isn't locked, or if it is locked and you are using the tag it was edited under.|,
lastUpdated => 1165448622

View file

@ -8,6 +8,12 @@ our $I18N = {
lastUpdated => 1134969093
},
'Unknown' => {
message => q|Unknown|,
lastUpdated => 1134969093,
context => q|meaning, we do not know who it is, and it is not Visitor|,
},
};
1;

View file

@ -3049,6 +3049,12 @@ or are under your current version tag.</p>
context => q|Label of the cancel button|
},
'Cancel' => {
message => q|Cancel|,
lastUpdated =>1092930637,
context => q|Label of the cancel button, with capital C|
},
'trash' => {
message => q|Trash|,
lastUpdated =>1211131614,