diff --git a/lib/WebGUI/Asset/MapPoint.pm b/lib/WebGUI/Asset/MapPoint.pm
new file mode 100644
index 000000000..d8ccb8f33
--- /dev/null
+++ b/lib/WebGUI/Asset/MapPoint.pm
@@ -0,0 +1,402 @@
+package WebGUI::Asset::MapPoint;
+
+=head1 LEGAL
+
+ -------------------------------------------------------------------
+ WebGUI is Copyright 2001-2009 Plain Black Corporation.
+ -------------------------------------------------------------------
+ Please read the legal notices (docs/legal.txt) and the license
+ (docs/license.txt) that came with this distribution before using
+ this software.
+ -------------------------------------------------------------------
+ http://www.plainblack.com info@plainblack.com
+ -------------------------------------------------------------------
+
+=cut
+
+use strict;
+use Tie::IxHash;
+use base 'WebGUI::AssetAspect::Installable','WebGUI::Asset';
+use WebGUI::Utility;
+
+# To get an installer for your wobject, add the Installable AssetAspect
+# See WebGUI::AssetAspect::Installable and sbin/installClass.pl for more
+# details
+
+=head1 NAME
+
+Package WebGUI::Asset::MapPoint
+
+=head1 DESCRIPTION
+
+Describe your New Asset's functionality and features here.
+
+=head1 SYNOPSIS
+
+use WebGUI::Asset::MapPoint;
+
+
+=head1 METHODS
+
+These methods are available from this class:
+
+=cut
+
+#-------------------------------------------------------------------
+
+=head2 definition ( session, definition )
+
+defines asset properties for New Asset instances. You absolutely need
+this method in your new Assets.
+
+=head3 session
+
+=head3 definition
+
+A hash reference passed in from a subclass definition.
+
+=cut
+
+sub definition {
+ my $class = shift;
+ my $session = shift;
+ my $definition = shift;
+ my $i18n = WebGUI::International->new( $session, "Asset_MapPoint" );
+ tie my %properties, 'Tie::IxHash', (
+ latitude => {
+ tab => "properties",
+ fieldType => "float",
+ label => $i18n->echo("Latitude"),
+ hoverHelp => $i18n->echo("The latitude of the point"),
+ noFormPost => 1,
+ },
+ longitude => {
+ tab => "properties",
+ fieldType => "float",
+ label => $i18n->echo("Longitude"),
+ hoverHelp => $i18n->echo("The longitude of the point"),
+ noFormPost => 1,
+ },
+ website => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("Website"),
+ hoverHelp => $i18n->echo("The URL to the location's website"),
+ },
+ address1 => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("Address 1"),
+ hoverHelp => $i18n->echo("The first line of the address"),
+ },
+ address2 => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("Address 2"),
+ hoverHelp => $i18n->echo("The second line of the address"),
+ },
+ city => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("City"),
+ hoverHelp => $i18n->echo("The city the point is located in"),
+ },
+ state => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("State/Province"),
+ hoverHelp => $i18n->echo("The state/provice the point is located in"),
+ },
+ zipCode => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("Zip/Postal Code"),
+ hoverHelp => $i18n->echo("The zip/postal code the point is located in"),
+ },
+ country => {
+ tab => "properties",
+ fieldType => "country",
+ label => $i18n->echo("Country"),
+ hoverHelp => $i18n->echo("The country the point is located in"),
+ },
+ phone => {
+ tab => "properties",
+ fieldType => "phone",
+ label => $i18n->echo("Phone"),
+ hoverHelp => $i18n->echo("The phone number of the location"),
+ },
+ fax => {
+ tab => "properties",
+ fieldType => "phone",
+ label => $i18n->echo("Fax"),
+ hoverHelp => $i18n->echo("The fax number of the location"),
+ },
+ email => {
+ tab => "properties",
+ fieldType => "email",
+ label => $i18n->echo("E-mail"),
+ hoverHelp => $i18n->echo("The e-mail address of the location"),
+ },
+ storageIdPhoto => {
+ tab => "properties",
+ fieldType => "image",
+ forceImageOnly => 1,
+ label => $i18n->echo("Photo"),
+ hoverHelp => $i18n->echo("A photo of the location"),
+ noFormPost => 1,
+ },
+ userDefined1 => {
+ fieldType => "hidden",
+ },
+ userDefined2 => {
+ fieldType => "hidden",
+ },
+ userDefined3 => {
+ fieldType => "hidden",
+ },
+ userDefined4 => {
+ fieldType => "hidden",
+ },
+ userDefined5 => {
+ fieldType => "hidden",
+ },
+ );
+ push @{$definition}, {
+ assetName => $i18n->get('assetName'),
+ icon => 'MapPoint.gif',
+ autoGenerateForms => 1,
+ tableName => 'MapPoint',
+ className => 'WebGUI::Asset::MapPoint',
+ properties => \%properties,
+ };
+ return $class->SUPER::definition( $session, $definition );
+} ## end sub definition
+
+#-------------------------------------------------------------------
+
+=head2 canEdit ( [userId] )
+
+Returns true if the user can edit this MapPoint. Only the owner or the
+group to edit the parent Map are allowed to edit MapPoint.
+
+=cut
+
+sub canEdit {
+ my $self = shift;
+ my $userId = shift || $self->session->user->userId;
+ return 1 if $userId eq $self->get('ownerUserId');
+ return $self->SUPER::canEdit( $userId );
+}
+
+#-------------------------------------------------------------------
+
+=head2 getMapInfo ( )
+
+Get a hash of info to be put into the parent Map. Must include
+AT LEAST the following keys:
+
+ assetId - The ID of the MapPoint
+ latitude - The latitude of the point
+ longitude - The longitude of the point
+ title - The title of the point
+ content - HTML content to show details about the point
+ url - The URL of the point
+
+The following keys are optional
+
+ canEdit - If true, the user is allowed to edit the MapPoint
+
+
+=cut
+
+sub getMapInfo {
+ my $self = shift;
+ my $var = {};
+
+ # Get asset properties
+ $var->{ url } = $self->getUrl;
+ $var->{ assetId } = $self->getId;
+ my @keys = qw( latitude longitude title );
+ for my $key ( @keys ) {
+ $var->{ $key } = $self->get( $key );
+ }
+
+ # Get permissions
+ $var->{ canEdit } = $self->canEdit;
+
+ # Process the template to get the content
+ my $template = $self->getParent->getViewPointTemplate;
+ $var->{ content } = $template->process( $self->getTemplateVars );
+ WebGUI::Macro::process( $self->session, \$var->{content} );
+
+ return $var;
+}
+
+#-------------------------------------------------------------------
+
+=head2 getTemplateVars ( )
+
+Get common template vars for this MapPoint
+
+=cut
+
+sub getTemplateVars {
+ my $self = shift;
+ my $var = $self->get;
+
+ # Add gateway to URL
+ $var->{ url } = $self->getUrl;
+
+ return $var;
+}
+
+#-------------------------------------------------------------------
+
+=head2 getTemplateVarsEditForm ( )
+
+Get the template vars for the MapPoint edit form
+
+=cut
+
+sub getTemplateVarsEditForm {
+ my $self = shift;
+ my $session = $self->session;
+ my $var = $self->getTemplateVars;
+
+ $var->{ form_header }
+ = WebGUI::Form::formHeader( $session )
+ . WebGUI::Form::hidden( $session, {
+ name => 'func',
+ value => 'ajaxEditPointSave',
+ } )
+ . WebGUI::Form::hidden( $session, {
+ name => 'assetId',
+ value => $self->getId,
+ defaultValue => 'new',
+ } )
+ ;
+ $var->{ form_footer } = WebGUI::Form::formFooter( $session );
+
+ # Stuff from this class's definition
+ my $definition = __PACKAGE__->definition($session)->[0]->{properties};
+ for my $key ( keys %{$definition} ) {
+ next if $definition->{$key}->{noFormPost};
+ $definition->{$key}->{name} = $key;
+ $definition->{$key}->{value} = $self->getValue($key);
+ $var->{ "form_$key" }
+ = WebGUI::Form::dynamicField( $session, %{$definition->{$key}} );
+ }
+
+ # Stuff from Asset
+ $var->{ "form_title" }
+ = WebGUI::Form::text( $session, {
+ name => "title",
+ value => $self->get("title"),
+ } );
+ $var->{ "form_synopsis" }
+ = WebGUI::Form::textarea( $session, {
+ name => "synopsis",
+ value => $self->get("synopsis"),
+ resizable => 0,
+ } );
+
+ # Fix storageIdPhoto because scripts do not get executed in ajax requests
+ $var->{ "form_storageIdPhoto" }
+ = '';
+ if ( $self->get('storageIdPhoto') ) {
+ my $storage = WebGUI::Storage->get( $self->session, $self->get('storageIdPhoto') );
+ $var->{ "currentPhoto" }
+ = sprintf '
', $storage->getUrl($storage->getFiles->[0]);
+ }
+
+ return $var;
+}
+
+#-------------------------------------------------------------------
+
+=head2 processAjaxEditForm ( )
+
+Process the Ajax Edit Form from the Map. If any errors occur, return
+an array reference of error messages.
+
+=cut
+
+sub processAjaxEditForm {
+ my $self = shift;
+ my $session = $self->session;
+ my $form = $self->session->form;
+ my $prop = {};
+
+ # Stuff from this class's definition
+ my $definition = __PACKAGE__->definition($session)->[0]->{properties};
+ for my $key ( keys %{$definition} ) {
+ my $field = $definition->{$key};
+ next if $field->{noFormPost};
+ $prop->{$key}
+ = $form->get($key,$field->{fieldType},$field->{defaultValue},$field);
+ }
+
+ # Stuff from Asset
+ $prop->{ title } = $form->get('title');
+ $prop->{ menuTitle } = $form->get('title');
+ $prop->{ synopsis } = $form->get('synopsis');
+ $prop->{ url } = $session->url->urlize( $self->getParent->getUrl . '/' . $prop->{title} );
+ $prop->{ ownerUserId } = $form->get('ownerUserId') || $session->user->userId;
+
+ $self->update( $prop );
+
+ # Photo magic
+ $session->log->info("BEEP!");
+ if ( $form->get('storageIdPhoto') ) {
+ $session->log->info("BOOP!");
+ my $storage;
+ if ( $self->get('storageIdPhoto') ) {
+ $storage = WebGUI::Storage->get( $session, $self->get('storageIdPhoto') );
+ $storage->deleteFile( $storage->getFiles->[0] );
+ }
+ else {
+ $storage = WebGUI::Storage->create( $session );
+ $self->update({ storageIdPhoto => $storage->getId });
+ }
+
+ $storage->addFileFromFormPost( 'storageIdPhoto', 1 );
+ }
+
+ return;
+}
+
+#-------------------------------------------------------------------
+
+=head2 view ( )
+
+Get the content to show in the map text box. This will not be called
+by www_view, but we may want to call it elsewhere for some reason.
+
+=cut
+
+sub view {
+ my $self = shift;
+ return "TODO";
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_view ( )
+
+Redirect the user to the correct Map with the appropriate focus point
+so that this point is automatically shown.
+
+=cut
+
+sub www_view {
+ my $self = shift;
+
+ $self->session->http->setRedirect(
+ $self->getParent->getUrl('focusOn=' . $self->getId )
+ );
+ return "redirect";
+}
+
+1;
+
+#vim:ft=perl
diff --git a/lib/WebGUI/Asset/Wobject/Map.pm b/lib/WebGUI/Asset/Wobject/Map.pm
new file mode 100644
index 000000000..f441fdc73
--- /dev/null
+++ b/lib/WebGUI/Asset/Wobject/Map.pm
@@ -0,0 +1,587 @@
+package WebGUI::Asset::Wobject::Map;
+
+$VERSION = "1.0.0";
+
+#-------------------------------------------------------------------
+# 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 Tie::IxHash;
+use WebGUI::International;
+use WebGUI::Utility;
+use base 'WebGUI::AssetAspect::Installable','WebGUI::Asset::Wobject';
+
+# To get an installer for your wobject, add the Installable AssetAspect
+# See WebGUI::AssetAspect::Installable and sbin/installClass.pl for more
+# details
+
+#-------------------------------------------------------------------
+
+=head2 definition ( )
+
+Define asset properties
+
+=cut
+
+sub definition {
+ my $class = shift;
+ my $session = shift;
+ my $definition = shift;
+ my $i18n = WebGUI::International->new( $session, 'Asset_Map' );
+
+ tie my %properties, 'Tie::IxHash', (
+ groupIdAddPoint => {
+ tab => "security",
+ fieldType => "group",
+ label => $i18n->echo("Group to Add Points"),
+ hoverHelp => $i18n->echo("Group that is allowed to add points to the map"),
+ },
+ mapApiKey => {
+ tab => "properties",
+ fieldType => "text",
+ label => $i18n->echo("Google Maps API Key"),
+ hoverHelp => $i18n->echo("The generated Google Maps API key for this site"),
+ defaultValue=> $class->getDefaultApiKey($session),
+ subtext => $i18n->echo(q{Get your Google Maps API key}),
+ },
+ mapHeight => {
+ tab => "display",
+ fieldType => "text",
+ label => $i18n->echo("Map Height"),
+ hoverHelp => $i18n->echo("The height of the generated map"),
+ defaultValue => '400px',
+ },
+ mapWidth => {
+ tab => "display",
+ fieldType => "text",
+ label => $i18n->echo("Map Width"),
+ hoverHelp => $i18n->echo("The width of the generated map"),
+ defaultValue => '100%',
+ },
+ startLatitude => {
+ tab => "display",
+ fieldType => "float",
+ label => $i18n->echo("Starting Latitude"),
+ hoverHelp => $i18n->echo("Latitude of the default starting point of the map"),
+ defaultValue => 43.074719,
+ },
+ startLongitude => {
+ tab => "display",
+ fieldType => "float",
+ label => $i18n->echo("Starting Longitude"),
+ hoverHelp => $i18n->echo("Longitude of the default starting point of the map"),
+ defaultValue => -89.384251,
+ },
+ startZoom => {
+ tab => "display",
+ fieldType => "intSlider",
+ minimum => 1,
+ maximum => 19,
+ label => $i18n->echo("Starting Zoom Level"),
+ hoverHelp => $i18n->echo("Zoom level of the default starting point of the map"),
+ },
+ templateIdEditPoint => {
+ tab => "display",
+ fieldType => "template",
+ namespace => "MapPoint/Edit",
+ label => $i18n->echo("Template to Edit Map Point"),
+ hoverHelp => $i18n->echo("Template to edit a map point."),
+ },
+ templateIdView => {
+ tab => "display",
+ fieldType => "template",
+ namespace => "Map/View",
+ label => $i18n->echo("Template to View"),
+ hoverHelp => $i18n->echo("Template to view the map"),
+ },
+ templateIdViewPoint => {
+ tab => "display",
+ fieldType => "template",
+ namespace => "MapPoint/View",
+ label => $i18n->echo("Template to View Map Point"),
+ hoverHelp => $i18n->echo("Template to view a map point. Will pop-up a box inside the map."),
+ },
+ workflowIdPoint => {
+ tab => "security",
+ fieldType => "workflow",
+ label => $i18n->echo("Workflow for Adding Points"),
+ hoverHelp => $i18n->echo("Commit workflow for adding points"),
+ },
+ );
+ push @{$definition}, {
+ assetName => $i18n->get('assetName'),
+ icon => 'Map.gif',
+ autoGenerateForms => 1,
+ tableName => 'Map',
+ className => 'WebGUI::Asset::Wobject::Map',
+ properties => \%properties
+ };
+ return $class->SUPER::definition( $session, $definition );
+} ## end sub definition
+
+#-------------------------------------------------------------------
+
+=head2 canAddPoint ( [userId] )
+
+Returns true if the user can add points to this map. C is the
+ID of the user to check, defaults to the current user.
+
+=cut
+
+sub canAddPoint {
+ my $self = shift;
+ my $userId = shift;
+ my $user = $userId
+ ? WebGUI::User->new( $self->session, $userId )
+ : $self->session->user
+ ;
+
+ return $user->isInGroup( $self->get("groupIdAddPoint") );
+}
+
+#----------------------------------------------------------------------------
+
+=head2 canEdit ( [userId] )
+
+Returns true if the user can edit this Map. C is the userId to
+check. If no userId is passed, will check the current user.
+
+Users can edit this map if they are part of the C group.
+
+Also checks if a user is adding a MapPoint and allows them to if they are
+part of the C group.
+
+=cut
+
+sub canEdit {
+ my $self = shift;
+ my $userId = shift;
+
+ my $form = $self->session->form;
+
+ if ( $form->get('func') eq "add" && $form->get( 'class' )->isa( "WebGUI::Asset::MapPoint" ) ) {
+ return $self->canAddPoint( $userId );
+ }
+ elsif ( $form->get('func') eq "editSave" && $form->get('assetId') eq "new" && $form->get( 'class' )->isa( 'WebGUI::Asset::MapPoint' ) ) {
+ return $self->canAddPoint( $userId );
+ }
+ else {
+ my $user = $userId
+ ? WebGUI::User->new( $self->session, $userId )
+ : $self->session->user
+ ;
+
+ return $user->isInGroup( $self->get("groupIdEdit") );
+ }
+}
+
+#-------------------------------------------------------------------
+
+=head2 getAllPoints ( )
+
+Get all the MapPoints for this Map. Returns an array reference of
+asset IDs.
+
+=cut
+
+sub getAllPoints {
+ my $self = shift;
+
+ my $assetIds = $self->getLineage(['children']);
+ return $assetIds;
+}
+
+#-------------------------------------------------------------------
+
+=head2 getDefaultApiKey ( session )
+
+Get the default API key for the Map.
+
+=cut
+
+sub getDefaultApiKey {
+ my $class = shift;
+ my $session = shift;
+
+ # Get the API key used in other Maps on the site
+ eval {
+ # Map may not exist yet!
+ my $defaultApiKey = $session->db->quickScalar(
+ "SELECT mapApiKey FROM Map LIMIT 1"
+ );
+
+ return $defaultApiKey;
+ };
+ return '';
+}
+
+#-------------------------------------------------------------------
+
+=head2 getEditPointTemplate ( )
+
+Get the template to edit a MapPoint. Returns a fully-prepared template
+
+=cut
+
+sub getEditPointTemplate {
+ my $self = shift;
+
+ if ( !$self->{_editPointTemplate} ) {
+ my $templateId = $self->get('templateIdEditPoint');
+ my $template
+ = WebGUI::Asset::Template->new( $self->session, $templateId );
+ $template->prepare;
+ $self->{_editPointTemplate} = $template;
+ }
+
+ return $self->{_editPointTemplate};
+}
+
+#-------------------------------------------------------------------
+
+=head2 getViewPointTemplate ( )
+
+Get the template to view a MapPoint. Returns a fully-prepared template
+that can be used multiple times in a Map.
+
+=cut
+
+sub getViewPointTemplate {
+ my $self = shift;
+
+ if ( !$self->{_viewPointTemplate} ) {
+ my $templateId = $self->get('templateIdViewPoint');
+ my $template
+ = WebGUI::Asset::Template->new( $self->session, $templateId );
+ $self->{_viewPointTemplate} = $template;
+ }
+
+ return $self->{_viewPointTemplate};
+}
+
+#-------------------------------------------------------------------
+
+=head2 getTemplateVars ( )
+
+Get the template variables for this asset
+
+=cut
+
+sub getTemplateVars {
+ my $self = shift;
+ my $var = {};
+
+ $var->{ url } = $self->getUrl;
+ $var->{ canAddPoint } = $self->canAddPoint;
+ $var->{ canEdit } = $self->canEdit;
+
+ return $var;
+}
+
+#-------------------------------------------------------------------
+
+=head2 loadMapApiTags ( )
+
+Load the Map API tags into the response. Load everything needed to
+create the map (but do not create the map itself).
+
+This is seperate for timing purposes. This part can be loaded in the HEAD or
+in the BODY, but the rest of the map must be loaded at a very specific
+time.
+
+=cut
+
+sub loadMapApiTags {
+ my $self = shift;
+ my $style = $self->session->style;
+ my $url = $self->session->url;
+
+ $style->setScript("http://www.google.com/jsapi?key=" . $self->get('mapApiKey'),{type=>"text/javascript"});
+ $style->setRawHeadTags(<<'ENDHTML');
+
+ENDHTML
+ $style->setScript('http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/release/src/markermanager.js', {type=>"text/javascript"});
+ $style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'),{type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/connection/connection-min.js'),{type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/json/json-min.js'),{type=>'text/javascript'});
+ $style->setScript($url->extras('map/map.js'),{type=>'text/javascript'});
+
+ return;
+}
+
+#-------------------------------------------------------------------
+
+=head2 prepareView ( )
+
+See WebGUI::Asset::prepareView() for details.
+
+=cut
+
+sub prepareView {
+ my $self = shift;
+ $self->SUPER::prepareView();
+ my $template = WebGUI::Asset::Template->new( $self->session, $self->get("templateIdView") );
+ $template->prepare;
+ $self->{_viewTemplate} = $template;
+}
+
+#-------------------------------------------------------------------
+
+=head2 view ( )
+
+method called by the www_view method. Returns a processed template
+to be displayed within the page style.
+
+=cut
+
+sub view {
+ my $self = shift;
+ my $session = $self->session;
+ my $style = $self->session->style;
+ my $i18n = WebGUI::International->new( $session, 'Asset_Map' );
+
+ $self->loadMapApiTags;
+ my $var = $self->getTemplateVars;
+
+ # Build the map container
+ my $mapHtml = sprintf '',
+ $self->getId,
+ $self->get('mapHeight'),
+ $self->get('mapWidth'),
+ ;
+
+ # The script to load the map into the container
+ $mapHtml .= sprintf <<'ENDHTML', $self->getId, $self->getUrl, $self->get('startLatitude'), $self->get('startLongitude'), $self->get('startZoom');
+
+ENDHTML
+
+ $var->{ map } = $mapHtml;
+
+ # Button to add a map point
+ $var->{ button_addPoint }
+ = WebGUI::Form::Button( $session, {
+ value => $i18n->echo("Add Point"),
+ id => sprintf( 'addPoint_%s', $self->getId ),
+ } );
+
+ # Button to set the map's default view
+ $var->{ button_setCenter }
+ = WebGUI::Form::Button( $session, {
+ value => $i18n->echo("Set Default Viewing Area"),
+ id => sprintf( 'setCenter_%s', $self->getId ),
+ } );
+
+ return $self->processTemplate( $var, undef, $self->{_viewTemplate} );
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_ajaxDeletePoint ( )
+
+Immediately remove a point from the map.
+
+=cut
+
+sub www_ajaxDeletePoint {
+ my ( $self ) = @_;
+ my $session = $self->session;
+ my $assetId = $self->session->form->get('assetId');
+ my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
+ $self->session->http->setMimeType('application/json');
+ return JSON->new->encode({error => 'You are not allowed to remove this point'})
+ unless $asset && $asset->canEdit;
+
+ $asset->purge;
+ return JSON->new->encode({message => 'Point deleted'});
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_ajaxEditPoint ( )
+
+Get the form to edit a point to the map
+
+=cut
+
+sub www_ajaxEditPoint {
+ my $self = shift;
+ my $session = $self->session;
+ my $form = $self->session->form;
+
+ my $asset;
+ if ( $form->get('assetId') eq "new" ) {
+ $asset = WebGUI::Asset->newByPropertyHashRef( $session, {
+ className => "WebGUI::Asset::MapPoint",
+ } );
+ }
+ else {
+ $asset = WebGUI::Asset->newByDynamicClass( $session, $form->get('assetId') );
+ }
+
+ my $output = $self->getEditPointTemplate->process( $asset->getTemplateVarsEditForm );
+ WebGUI::Macro::process( $session, \$output );
+ return $output;
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_ajaxEditPointSave ( )
+
+Process the form to edit a point to the map
+
+=cut
+
+sub www_ajaxEditPointSave {
+ my $self = shift;
+ my $session = $self->session;
+ my $form = $self->session->form;
+
+ $session->http->setMimeType("text/html");
+
+ my $assetId = $form->get('assetId');
+ my $asset;
+ if ( $assetId eq "new" ) {
+ return JSON->new->encode({message => "Error"})
+ unless $self->canAddPoint;
+
+ $asset = $self->addChild( {
+ className => 'WebGUI::Asset::MapPoint',
+ } );
+ }
+ else {
+ $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
+ return JSON->new->encode({message => "Error"})
+ unless $asset && $asset->canEdit;
+ }
+
+ my $errors = $asset->processAjaxEditForm;
+
+ return JSON->new->encode($asset->getMapInfo);
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_ajaxSetCenter ( )
+
+Set the center of the map, ajax-style.
+
+=cut
+
+sub www_ajaxSetCenter {
+ my $self = shift;
+ my $session = $self->session;
+ my $form = $self->session->form;
+ my $i18n = WebGUI::International->new( $session, 'Asset_Map' );
+
+ $session->http->setMimeType("application/json");
+
+ return JSON->new->encode({message => $i18n->echo("No permissions")})
+ unless $self->canEdit;
+
+ $self->update({
+ startLatitude => $form->get("startLatitude"),
+ startLongitude => $form->get("startLongitude"),
+ startZoom => $form->get("startZoom"),
+ });
+
+ return JSON->new->encode({message => $i18n->echo("Success!")});
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_ajaxSetPointLocation ( )
+
+Set the location of a point
+
+=cut
+
+sub www_ajaxSetPointLocation {
+ my $self = shift;
+ my $session = $self->session;
+ my $form = $self->session->form;
+ my $i18n = WebGUI::International->new( $session, 'Asset_Map' );
+
+ $session->http->setMimeType("application/json");
+
+ my $assetId = $form->get('assetId');
+ my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
+ $asset->update( {
+ latitude => $form->get('latitude'),
+ longitude => $form->get('longitude'),
+ } );
+
+ return JSON->new->encode( {message => $i18n->echo("Success!")} );
+}
+
+1;
+
+#vim:ft=perl
diff --git a/lib/WebGUI/i18n/English/Asset_Map.pm b/lib/WebGUI/i18n/English/Asset_Map.pm
new file mode 100644
index 000000000..4cd4de614
--- /dev/null
+++ b/lib/WebGUI/i18n/English/Asset_Map.pm
@@ -0,0 +1,14 @@
+package WebGUI::i18n::English::Asset_Map;
+
+use strict;
+
+our $I18N = {
+ 'assetName' => {
+ message => "Map",
+ lastUpdated => 0,
+ context => "Name of this asset",
+ },
+};
+
+1;
+#vim:ft=perl
diff --git a/lib/WebGUI/i18n/English/Asset_MapPoint.pm b/lib/WebGUI/i18n/English/Asset_MapPoint.pm
new file mode 100644
index 000000000..0a2bf30b7
--- /dev/null
+++ b/lib/WebGUI/i18n/English/Asset_MapPoint.pm
@@ -0,0 +1,15 @@
+package WebGUI::i18n::English::Asset_MapPoint;
+
+use strict;
+
+our $I18N = {
+
+ 'assetName' => {
+ message => "MapPoint",
+ lastUpdated => 0,
+ context => "Name of this asset",
+ },
+};
+
+1;
+#vim:ft=perl
diff --git a/www/extras/map/map.js b/www/extras/map/map.js
new file mode 100644
index 000000000..28f43e4eb
--- /dev/null
+++ b/www/extras/map/map.js
@@ -0,0 +1,237 @@
+
+// Initialize namespace
+if (typeof WebGUI == "undefined") {
+ var WebGUI = {};
+}
+if (typeof WebGUI.Map == "undefined") {
+ WebGUI.Map = {};
+}
+
+// Keep track of all points on all maps and how to focus on them
+WebGUI.Map.markers = {};
+
+/**
+ * WebGUI.Map.deletePoint( map, mgr, mapUrl, marker )
+ * Delete a point from the map.
+ * NOTE: We assume the user has already confirmed this action
+ */
+WebGUI.Map.deletePoint
+= function ( map, mgr, mapUrl, marker ) {
+ var callback = function ( text, code ) {
+ var response = YAHOO.lang.JSON.parse( text );
+ // remove the marker from the map
+ if ( !response.error ) {
+ mgr.removeMarker( marker );
+ }
+ };
+ GDownloadUrl( mapUrl + '?func=ajaxDeletePoint;assetId=' + marker.assetId, callback);
+};
+
+/**
+ * WebGUI.Map.editPoint( map, mgr, mapUrl, marker )
+ * Edit a point on the map.
+ * up the edit box. mgr is the Marker Manager to add the marker
+ * to. mapUrl is the URL of the map asset
+ */
+WebGUI.Map.editPoint
+= function ( map, mgr, mapUrl, marker ) {
+ var assetId = '';
+ if ( !marker ) {
+ marker = new GMarker( map.getCenter(), { draggable: true } );
+ mgr.addMarker( marker, 1 );
+ mgr.refresh();
+ assetId = "new";
+ marker.assetId = "new";
+ GEvent.addListener( marker, "dragend", function (latlng) {
+ WebGUI.Map.setPointLocation( marker, latlng, mapUrl );
+ } );
+ }
+ else {
+ assetId = marker.assetId;
+ }
+
+ // Callback should open the window with the form
+ var callback = function (text, code) {
+ marker.openInfoWindowHtml( text );
+ GEvent.addListener( marker, "infowindowbeforeclose", function () {
+ WebGUI.Map.editPointSave( map, mgr, mapUrl, marker );
+ });
+ };
+
+ // Get the form
+ GDownloadUrl( mapUrl + '?func=ajaxEditPoint;assetId=' + assetId, callback );
+};
+
+/**
+ * WebGUI.Map.editPointSave( map, mgr, mapUrl, marker )
+ * Save the edit form.
+ */
+WebGUI.Map.editPointSave
+= function ( map, mgr, mapUrl, marker ) {
+ var iwin = map.getInfoWindow();
+ var form = iwin.getContentContainers()[0].getElementsByTagName('form')[0];
+
+ // Make ajax request
+ var callback = {
+ upload: function (o) {
+ // Update marker info
+ console.log(o.responseText);
+ var point = YAHOO.lang.JSON.parse( o.responseText );
+ GEvent.clearListeners( marker, "click" );
+ GEvent.clearListeners( marker, "infowindowbeforeclose" );
+
+ var infoWin = document.createElement( "div" );
+ infoWin.innerHTML = point.content;
+ marker.infoWin = infoWin;
+ if ( point.canEdit ) {
+ var editButton = document.createElement("input");
+ editButton.type = "button";
+ editButton.value = "Edit";
+ GEvent.addDomListener( editButton, "click", function () {
+ WebGUI.Map.editPoint( map, mgr, mapUrl, marker );
+ } );
+ infoWin.appendChild( editButton );
+
+ var deleteButton = document.createElement("input");
+ deleteButton.type = "button";
+ deleteButton.value = "Delete"; // Replace with i18n
+ GEvent.addDomListener( deleteButton, "click", function () {
+ if ( confirm("Are you sure you want to delete this point?") ) {
+ WebGUI.Map.deletePoint( map, mgr, mapUrl, marker );
+ }
+ } );
+ divButton.appendChild( deleteButton );
+ }
+ marker.bindInfoWindow( infoWin );
+
+ // Make sure the point has a Lat/Lng in case the user doesn't
+ // move it after initially creating it.
+ if ( marker.assetId == "new" ) {
+ marker.assetId = point.assetId;
+ WebGUI.Map.setPointLocation( marker, marker.getLatLng(), mapUrl );
+ }
+ marker.assetId = point.assetId;
+ }
+ };
+ // In case our form does not have a file upload in it
+ callback["success"] = callback["upload"];
+
+ YAHOO.util.Connect.setForm( form, 1 );
+ YAHOO.util.Connect.asyncRequest( 'POST', mapUrl, callback );
+};
+
+/**
+ * WebGUI.Map.focusOn( assetId )
+ * Pan the appropriate map to view the appropriate map point
+ */
+WebGUI.Map.focusOn
+= function ( assetId ) {
+ var marker = WebGUI.Map.markers[assetId];
+ var map = marker.map;
+ var infoWin = marker.infoWin;
+ if ( map.getZoom() < 5 ) {
+ map.setZoom(6);
+ }
+ map.panTo( marker.getLatLng() );
+ marker.openInfoWindow( marker.infoWin );
+};
+
+/**
+ * WebGUI.Map.preparePoints ( map, mgr, mapUrl, points )
+ * Prepare the points from WebGUI into Google Map GMarkers
+ */
+WebGUI.Map.preparePoints
+= function ( map, mgr, mapUrl, points ) {
+ // Transform points into markers
+ var markers = [];
+ for ( var i = 0; i < points.length; i++ ) (function(i){ // Create closure for callbacks
+ var point = points[i];
+ var latlng = new GLatLng( point.latitude, point.longitude );
+ var marker = new GMarker( latlng, {
+ title: point.title,
+ draggable: point.canEdit
+ } );
+ marker.assetId = point.assetId;
+ marker.map = map;
+
+ // Create info window
+ var infoWin = document.createElement( "div" );
+ infoWin.innerHTML = point.content;
+ marker.infoWin = infoWin;
+
+ // Make editable features
+ if ( point.canEdit ) {
+ var divButton = document.createElement('div');
+
+ var editButton = document.createElement("input");
+ editButton.type = "button";
+ editButton.value = "Edit"; // Replace with i18n
+ GEvent.addDomListener( editButton, "click", function () {
+ WebGUI.Map.editPoint( map, mgr, mapUrl, marker );
+ } );
+ divButton.appendChild( editButton );
+
+ var deleteButton = document.createElement("input");
+ deleteButton.type = "button";
+ deleteButton.value = "Delete"; // Replace with i18n
+ GEvent.addDomListener( deleteButton, "click", function () {
+ if ( confirm("Are you sure you want to delete this point?") ) {
+ WebGUI.Map.deletePoint( map, mgr, mapUrl, marker );
+ }
+ } );
+ divButton.appendChild( deleteButton );
+
+ infoWin.appendChild( divButton );
+ GEvent.addListener( marker, "dragend", function (latlng) {
+ WebGUI.Map.setPointLocation( marker, latlng, mapUrl );
+ } );
+ }
+
+ // Keep info
+ WebGUI.Map.markers[point.assetId] = marker;
+
+ marker.bindInfoWindow( infoWin );
+
+ markers.push(marker);
+ })(i);
+ return markers;
+};
+
+/**
+ * WebGUI.Map.setCenter ( map, baseUrl )
+ * Set the new center point and zoom level of the map.
+ * map is the Google Map object
+ * baseUrl is the base URL to the Map asset
+ */
+WebGUI.Map.setCenter
+= function ( map, baseUrl ) {
+ var url = baseUrl + '?func=ajaxSetCenter';
+ var center = map.getCenter();
+ url = url + ';startLatitude=' + center.lat()
+ + ';startLongitude=' + center.lng()
+ + ';startZoom=' + map.getZoom()
+ ;
+ var callback = function ( text, code ) {
+ // TODO: Notify the poor user
+ };
+ GDownloadUrl(url,callback);
+};
+
+/**
+ * WebGUI.Map.setPointLocation( marker, latlng, mapUrl, assetId )
+ * Update the point's location in the database.
+ */
+WebGUI.Map.setPointLocation
+= function ( marker, latlng, mapUrl, assetId ) {
+ var url = mapUrl + '?func=ajaxSetPointLocation'
+ + ';assetId=' + marker.assetId
+ + ';latitude=' + latlng.lat()
+ + ';longitude=' + latlng.lng()
+ ;
+ var callback = function ( text, code ) {
+ // TODO: Notify the poor user
+ };
+ GDownloadUrl(url, callback);
+};
+
+