Skip to content

Instantly share code, notes, and snippets.

@marekmurawski
Last active December 11, 2015 01:08
Show Gist options
  • Save marekmurawski/4520898 to your computer and use it in GitHub Desktop.
Save marekmurawski/4520898 to your computer and use it in GitHub Desktop.
Google Maps v3 FRONTEND EDITOR snippet for Wolf CMS requires MultiEdit plugin.
<?php
/**
* Google Maps v3 FRONTEND EDITOR snippet for Wolf CMS
*
* @author Marek Murawski <http://marekmurawski.pl>
* @copyright Marek Murawski, 2013
* @license http://www.gnu.org/licenses/gpl.html GPLv3 license
*
* REQUIRES:
* =========
* - MultiEdit 0.1.1 plugin for Wolf CMS <http://www.wolfcms.org/repository/120>
* - jQuery in layout head
* eg. <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
*
* INSTALLATION:
* =============
*
* 1. Make sure MultiEdit plugin is installed and activated.
*
* 2. Copy the contents of this file into newly created Snippet
* let's say "mmGMaps"
*
* 3. In the "body" page part of page insert:
* <?php $this->includeSnippet('mmGMaps'); ?>
*
* 4. Create the following empty page parts in the same page:
* - map_settings
* - map_infowindow
* - map_path
* (these are default, can be configured below)
*
* 5. DONE! Visit the page and play around.
*
*
* TROUBLESHOOTING:
* ================
*
* If you mess up with something try config in appropriate
* page parts OR delete the whole contents of those page parts
*/
/*
* CONFIGURATION OPTIONS
*/
//map height (can be anything understandable by CSS)
$hei = '400px';
//map width (can be anything understandable by CSS)
$wid = '100%';
// permissions needed to edit the frontend map
$needed_permission = 'admin_edit';
// name of page part to store map settings
$settings_in_part = 'map_settings';
// name of page part to store shape
$path_in_part = 'map_path';
// name of page part to store infowindow text
$infowindow_text_in_part = 'map_infowindow';
// these parameters specify google's map interface language
$language = 'en';
$region = 'us';
/*
* THE SNIPPET
*/
/**
* Getting needed page_part contents
* for security reasons you SHOULD NOT use
*
* echo $this->content('path');
*
* because this eval's PHP code which
* could be injected somehow by unpolite user
*/
$warnings = '';
$default_path =
'new google.maps.LatLng(52.69478333, 16.74786964),
new google.maps.LatLng(52.71469791, 16.74796366),
new google.maps.LatLng(52.71451376, 16.75784434),
new google.maps.LatLng(52.70652961, 16.76566504),
new google.maps.LatLng(52.71445747, 16.77383044),
new google.maps.LatLng(52.71448121, 16.78405488),
new google.maps.LatLng(52.69468923, 16.78409539),
new google.maps.LatLng(52.69472702, 16.77355897),
new google.maps.LatLng(52.70350304, 16.77349393),
new google.maps.LatLng(52.69761141, 16.76587576),
new google.maps.LatLng(52.70378664, 16.75791424),
new google.maps.LatLng(52.69501994, 16.75795575)';
$map_path_points = Record::findOneFrom( 'PagePart', 'page_id=:id AND name=:name', array(
':id' => $this->id,
':name' => $path_in_part
) );
if ( $map_path_points ) {
$lines = explode( PHP_EOL, $map_path_points->content_html );
if ( count( $lines ) < 3 ) {
$map_path_points_text = $default_path;
$warnings .= '<li>The default shape has been created, you can modify it now</li>';
}
else {
$map_path_points_text = $map_path_points->content_html;
}
}
else {
$warnings .= '<li>You have to create <b>' . $path_in_part . '</b> page part in this page (id:' . $this->id . ') if you want to have the polygon shape saved.</li>';
$map_path_points_text = $default_path;
}
$map_infowindow_text_part = Record::findOneFrom( 'PagePart', 'page_id=:id AND name=:name', array(
':id' => $this->id,
':name' => $infowindow_text_in_part
) );
if ( $map_infowindow_text_part ) {
if ( strlen( $map_infowindow_text_part->content_html ) < 2 ) {
$map_infowindow_text = 'Marek Murawski';
$warnings .= '<li>Added default infowindow text</li>';
}
else {
$map_infowindow_text = $map_infowindow_text_part->content_html;
}
}
else {
$warnings .= '<li>You have to create <b>' . $infowindow_text_in_part . '</b> page part in this page (id:' . $this->id . ') if you want to have the infowindow text saved.</li>';
$map_infowindow_text = 'Create <b>' . $infowindow_text_in_part . '</b> page part in this page <br/> to be able to change and store this text!';
}
$map_settings_part = Record::findOneFrom( 'PagePart', 'page_id=:id AND name=:name', array(
':id' => $this->id,
':name' => $settings_in_part
) );
if ( $map_settings_part ) {
$map_settings = $map_settings_part->content_html;
}
else {
$warnings .= '<li>You have to create <b>' . $settings_in_part . '</b> page part in this page (id:' . $this->id . ') if you want to have the map settings (zoom, position, etc) saved.</li>';
$map_settings = '';
}
?>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&amp;language=<?php echo$language?>&amp;region=<?php echo$region?>"></script>
<script type="text/javascript">
// Variables
var map;
var thePolygon;
var newSettingsString;
var theMarker;
var theInfowindow;
// Marker options
var marker_lat = 52.67930597087852;
var marker_lng = 16.820114025878866;
var marker_title = "MM";
var marker_icon = 'http://marekmurawski.pl/thmm/w32/mm_icon_64.png';
var infowindow_open = true;
var infowindow_content = "<?php echo $map_infowindow_text ?>";
var map_zoom = 12;
var map_min_zoom = 5;
var map_max_zoom = 18;
var map_center_lat = 52.69179385129268;
var map_center_lng = 16.817710766601515;
var map_type_id = 'SATELLITE';
var shape_stroke_color = "#FF8800";
var shape_stroke_opacity = 1.0;
var shape_stroke_weight = 3;
var shape_fill_color = "#FF8800";
var shape_fill_opacity = 0.35;
// OVERRIDEN values
<?php
echo $map_settings;
?>
// shape definition
var shape_path = [
<?php
echo $map_path_points_text;
?>
];
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
/**
* Deletes the nearest node based on distance from click point
*/
function deleteNode(mouseEvent) {
var clickPoint = map.getProjection().fromLatLngToPoint(mouseEvent.latLng);
var delPath = thePolygon.getPath();
var minDist = 512;
var selectedIndex = -1;
// disallow 2-vertex polygons - triangle is minimum
if (delPath.getLength() < 4) {alert('Cannot delete more nodes'); return false;}
// iterate through all nodes to find index of the nearest one
for (var n = 0 ; n < delPath.getLength() ; n++) {
var nodePoint = map.getProjection().fromLatLngToPoint(delPath.getAt(n));
var dist = Math.sqrt(Math.pow(Math.abs(clickPoint.x - nodePoint.x),2) + Math.pow(Math.abs(clickPoint.y - nodePoint.y),2));
if (dist < minDist) {
minDist = dist;
selectedIndex = n;
}
}
// actually remove found node from the path
delPath.removeAt(selectedIndex);
// invoke path saving after removing node
$('#path_store_button').trigger('click');
} // end of deleteNode
<?php endif; ?>
// prepare the thePolygon
thePolygon = new google.maps.Polygon({
path: shape_path,
strokeColor: shape_stroke_color,
strokeOpacity: shape_stroke_opacity,
strokeWeight: shape_stroke_weight,
fillColor: shape_fill_color,
fillOpacity: shape_fill_opacity,
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
editable: true
<?php else: ?>
editable: false
<?php endif; ?>
});
function initialize_gmap() {
// Map options
var myOptions = {
zoom: map_zoom,
minZoom: map_min_zoom,
maxZoom: map_max_zoom,
center: new google.maps.LatLng(map_center_lat, map_center_lng),
mapTypeId: strToMapTypeId(map_type_id),
streetViewControl: false,
overviewMapControl: false,
panControl: false
};
map = new google.maps.Map(document.getElementById("map-canvas"),myOptions);
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
// add function to handle thePolygon's mouse events
google.maps.event.addListener(thePolygon.getPath(), 'set_at', function() {
// simulate clicking button
$('#path_store_button').trigger('click');
});
google.maps.event.addListener(thePolygon.getPath(), 'insert_at', function() {
// simulate clicking button
$('#path_store_button').trigger('click');
});
<?php endif; ?>
// attach the thePolygon to our map
thePolygon.setMap(map);
// setup the Marker
theMarker = new google.maps.Marker({
position: new google.maps.LatLng(marker_lat, marker_lng),
map: map,
icon: marker_icon,
title: marker_title,
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
draggable: true
<?php else: ?>
draggable: false
<?php endif; ?>
});
// setup the Infowindow
theInfowindow = new google.maps.InfoWindow({
content: infowindow_content,
disableAutoPan: true //to avoid panning at start and storing settings
});
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
//Add listener to marker for reverse geocoding
google.maps.event.addListener(theMarker, 'dragend', function() {
$('#map_settings_store_button').trigger('click');
});
google.maps.event.addListener(map, 'dragend', function() {
$('#map_settings_store_button').trigger('click');
});
google.maps.event.addListener(map, 'zoom_changed', function() {
$('#map_settings_store_button').trigger('click');
populateSettingsFields();
});
google.maps.event.addListener(theInfowindow, 'closeclick', function() {
infowindow_open = false;
$('#map_settings_store_button').trigger('click');
});
google.maps.event.addListener(theMarker, 'click', function() {
//alert('close');
theInfowindow.open(map, theMarker);
infowindow_open = true;
$('#map_settings_store_button').trigger('click');
});
<?php endif; ?>
// and open it at the Marker
if (infowindow_open) {theInfowindow.open(map, theMarker);}
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
populateSettingsFields();
$('#map_settings_min_zoom').val(map_min_zoom);
$('#map_settings_max_zoom').val(map_max_zoom);
$('#shape_settings_stroke_color').val(shape_stroke_color);
$('#shape_settings_stroke_weight').val(shape_stroke_weight);
$('#shape_settings_stroke_opacity').val(shape_stroke_opacity);
$('#shape_settings_fill_color').val(shape_fill_color);
$('#shape_settings_fill_opacity').val(shape_fill_opacity);
$('#marker_settings_icon').val(marker_icon);
$('#marker_settings_title').val(marker_title);
$("#map_settings_map_type_id option[value='" + map_type_id + "']").attr("selected","selected");
// listen for rightclicks on the map area to delete the nearest points
google.maps.event.addListener(map,'rightclick',deleteNode);
<?php endif ?>
}
// init the map
google.maps.event.addDomListener(window, "load", initialize_gmap);
<?php
/**
* EDIT HANDLING PART - Only rendered for users with valid permissions
*/
if ( AuthUser::hasPermission( $needed_permission ) ):
?>
$('#path_store_button').live('click', function() {
var output = '';
var object = thePolygon.getPath().b;
//iterate through all path nodes and add them to js array
for (property in object) {
output += 'new google.maps.LatLng' + object[property]+','+'\n';
}
// trim last comma and newline
output = output.substring(0, output.length - 2)+'\n';
$('#path_textarea').val(output);
savePath();
});
var savePath = function() {
var request = $.ajax({
url: '<?php echo URL_PUBLIC . ADMIN_DIR . '/plugin/multiedit/setvalue/' ?>',
type: 'POST',
dataType: 'json',
data: {
item: "part-<?php echo $this->id; ?>_partname_<?php echo $path_in_part ?>",
value: $('#path_textarea').val()
},
beforeSend: function() {
$('#path_error,#path_success').hide();
$('#path_progress').html('Saving polygon settings...');
$('#path_progress').fadeIn('fast');
},
success: function( data ) {
if (data.status == 'OK') {
$('#path_success').html('Polygon shape saved successfully.');
$('#path_success').fadeIn('fast');
} else {
$('#path_success,#path_success').hide();
}
},
error: function( data ) {
$('#path_error').html('Error while storing path! Make sure the <b>"<?php echo $path_in_part; ?>"</b> page part exists in this page.');
$('#path_error').fadeIn('fast');
}
})
};
var saveSettings = function() {
var request = $.ajax({
url: '<?php echo URL_PUBLIC . ADMIN_DIR . '/plugin/multiedit/setvalue/' ?>',
type: 'POST',
dataType: 'json',
data: {
item: "part-<?php echo $this->id; ?>_partname_<?php echo $settings_in_part ?>",
value: newSettingsString
},
beforeSend: function() {
$('#path_error,#path_success').hide();
$('#path_progress').html('Saving map settings...');
$('#path_progress').fadeIn('fast');
},
success: function( data ) {
if (data.status == 'OK') {
$('#path_success').html('Map settings saved successfully.');
$('#path_success').fadeIn('fast');
} else {
$('#path_success,#path_success').hide();
}
},
error: function( data ) {
$('#path_error').html('Error while storing map settings! Make sure the <b>"<?php echo $settings_in_part; ?>"</b> page part exists in this page.');
$('#path_error').fadeIn('fast');
}
})
};
var saveInfowindow = function() {
var request = $.ajax({
url: '<?php echo URL_PUBLIC . ADMIN_DIR . '/plugin/multiedit/setvalue/' ?>',
type: 'POST',
dataType: 'json',
data: {
item: "part-<?php echo $this->id; ?>_partname_<?php echo $infowindow_text_in_part ?>",
value: $('#map_infowindow_textarea').val()
},
beforeSend: function() {
$('#path_error,#path_success').hide();
$('#path_progress').html('Saving infowindow text...');
$('#path_progress').fadeIn('fast');
},
success: function( data ) {
if (data.status == 'OK') {
$('#path_success').html('Infowindow text saved successfully.');
$('#path_success').fadeIn('fast');
} else {
$('#path_success,#path_success').hide();
}
},
error: function( data ) {
$('#path_error').html('Error while storing infowindow text! Make sure the <b>"<?php echo $infowindow_text_in_part; ?>"</b> page part exists in this page.');
$('#path_error').fadeIn('fast');
}
})
};
$('#map_settings_store_button').live('click', function() {
var output = '\t';
if ($('#marker_settings_icon').val().length > 0) mURL = '"' + $('#marker_settings_icon').val() + '"'
else mURL = 'null';
output += "var marker_lat = " + theMarker.getPosition().lat() + ';' + '\n\t';
output += "var marker_lng = " + theMarker.getPosition().lng() + ';' +'\n\t';
output += "var marker_title = '" + theMarker.getTitle() + "';" +'\n\t';
output += "var marker_icon = " + mURL + ";" +'\n\t';
output += "var map_center_lat = " + map.getCenter().lat() + ';' + '\n\t';
output += "var map_center_lng = " + map.getCenter().lng() + ';' + '\n\t';
output += "var map_zoom = " + map.getZoom() + ';' + '\n\t';
output += "var map_min_zoom = " + $('#map_settings_min_zoom').val() + ';' + '\n\t';
output += "var map_max_zoom = " + $('#map_settings_max_zoom').val() + ';' + '\n\t';
output += "var map_type_id = '" + $('#map_settings_map_type_id').val() + "';" + '\n\t';
output += "var shape_stroke_color = '" + $('#shape_settings_stroke_color').val() + "';" + '\n\t';
output += "var shape_stroke_weight = " + $('#shape_settings_stroke_weight').val() + ";" + '\n\t';
output += "var shape_stroke_opacity = " + $('#shape_settings_stroke_opacity').val() + ";" + '\n\t';
output += "var shape_fill_color = '" + $('#shape_settings_fill_color').val() + "';" + '\n\t';
output += "var shape_fill_opacity = " + $('#shape_settings_fill_opacity').val() + ";" + '\n\t';
var opcl = (infowindow_open) ? 'true' : 'false';
output += "var infowindow_open = " + opcl + ';' + '\n\t';
$('#map_settings_textarea').val(output);
newSettingsString = output;
saveSettings();
});
$('#infowindow_store_button').live('click', function() {
saveInfowindow();
});
$('#map_infowindow_textarea').live('keyup', function() {
theInfowindow.setContent($('#map_infowindow_textarea').val());
});
$('#map_infowindow_textarea').live('change', function() {
saveInfowindow();
});
$('.map_option').live('change', function() {
theMarker.setTitle($('#marker_settings_title').val());
theMarker.setIcon($('#marker_settings_icon').val());
var shapeOpts = {
fillColor : $('#shape_settings_fill_color').val(),
fillOpacity : parseFloat($('#shape_settings_fill_opacity').val()),
strokeColor : $('#shape_settings_stroke_color').val(),
strokeOpacity : parseFloat($('#shape_settings_stroke_opacity').val()),
strokeWeight : parseInt($('#shape_settings_stroke_weight').val())
};
thePolygon.setOptions(shapeOpts);
var currZoom = parseInt($('#map_settings_zoom').val());
var mapOpts = {
minZoom : parseInt($('#map_settings_min_zoom').val()),
maxZoom : parseInt($('#map_settings_max_zoom').val()),
mapTypeId : strToMapTypeId($('#map_settings_map_type_id').val())
};
$('#map_settings_zoom').attr('max',mapOpts.maxZoom);
$('#map_settings_zoom').attr('min',mapOpts.minZoom);
map.setOptions(mapOpts);
map.setZoom(currZoom);
});
function populateSettingsFields() {
$('#map_settings_zoom').val(map.getZoom());
$('#map_infowindow_textarea').val(infowindow_content);
//$('#shape_settings_fill_color').val()
//$('#shape_settings_fill_opacity').val
}
<?php endif; // END OF edit handling part ?>
function strToMapTypeId(string) {
var mtID = google.maps.MapTypeId.HYBRID;
if (string=='ROADMAP') mtID = google.maps.MapTypeId.ROADMAP;
if (string=='SATELLITE') mtID = google.maps.MapTypeId.SATELLITE;
if (string=='TERRAIN') mtID = google.maps.MapTypeId.TERRAIN;
return mtID;
}
function mapTypeIdToStr(mtID) {
if (mtID == google.maps.MapTypeId.HYBRID) string = 'HYBRID'
else if (mtID == google.maps.MapTypeId.ROADMAP) string = 'ROADMAP'
else if (mtID == google.maps.MapTypeId.SATELLITE) string = 'SATELLITE'
else if (mtID == google.maps.MapTypeId.TERRAIN) string = 'TERRAIN'
else {
alert ('Unknown map type!')
return null;
}
}
</script>
<div class="map-canvas" id="map-canvas" style="width:<?php echo $wid; ?>;height:<?php echo $hei; ?>"></div>
<?php if ( AuthUser::hasPermission( $needed_permission ) ): ?>
<style>
.map_messages_box { box-sizing: border-box; position: relative; height: 30px; font-size: 15px; width: <?php echo $wid; ?>; }
.map_message { position: absolute; font-size: 15px; padding: 4px; width: <?php echo $wid; ?>; }
.map_warnings_box { box-sizing: border-box; position: relative; font-size: 13px; width: 100%; background: #ff8888; color: black; padding: 8px}
.map_options_box { box-sizing: border-box; position: relative; width: <?php echo $wid; ?>; padding: 12px; margin: 5px 0px; background-color: #eee;}
.map_options_box p {font-size: 13px;}
.map_set_table {width: 100%}
.map_set_table select,
.map_set_table input {width: 70px}
.map_set_table textarea {width: 100%}
.map_set_table td {padding: 2px 8px; font-size: 13px;}
.map_set_table td.quarter {width: 25%;}
.map_set_table td.rlabel {text-align: right}
.map_set_table tr.section td {background-color: #333; color: #CCC; font-weight: bold;}
</style>
<div class="map_messages_box">
<div class="map_message" id="path_progress" style="background: #CCC; color: #666; display: none;">Saving the polygon!</div>
<div class="map_message" id="path_success" style="background: greenyellow; color: darkgreen; display: none;">Polygon saved successfully!</div>
<div class="map_message" id="path_error" style="background: red; color: white; display: none;">Error while saving polygon!</div>
</div>
<div class="map_options_box">
<p><b>You are logged in and can edit this map.</b></p>
<?php if ( $warnings ): ?>
<div class="map_warnings_box">
<p><b>However:</b></p>
<ul>
<?php echo $warnings; ?>
</ul>
</div>
<?php endif; ?>
<p>To change the shape move control points, to <b>delete</b> the point <b>right-click</b> near it outside the polygon.
Map type, position and zoom level are stored automatically as well as polygon properties. Try hitting F5 to see the effect.</p>
<table class="map_set_table">
<tbody>
<tr class="section">
<td colspan="4">
Map settings
</td>
</tr>
<tr>
<td class="quarter">
Min zoom
</td>
<td class="quarter">
Curr. zoom
</td>
<td class="quarter">
Max zoom
</td>
<td class="quarter">
Map type
</td>
</tr>
<tr>
<td>
<input type="number" class="map_option" min="0" max="28" value="" id="map_settings_min_zoom" />
</td>
<td>
<input type="number" class="map_option" value="" id="map_settings_zoom" />
</td>
<td>
<input type="number" class="map_option" min="0" max="28" value="" id="map_settings_max_zoom" />
</td>
<td>
<select id="map_settings_map_type_id" class="map_option" >
<option value="HYBRID">Hybrid</option>
<option value="ROADMAP">Road</option>
<option value="SATELLITE">Satellite</option>
<option value="TERRAIN">Terrain</option>
</select>
</td>
</tr>
<tr class="section">
<td colspan="4">
Shape settings
</td>
</tr>
<tr>
<td class="rlabel">
Stroke color
</td>
<td>
<input type="color" class="map_option" id="shape_settings_stroke_color" />
</td>
<td class="rlabel">
Fill color
</td>
<td>
<input type="color" class="map_option" id="shape_settings_fill_color" />
</td>
</tr>
<tr>
<td class="rlabel">
Stroke opacity
</td>
<td>
<input type="number" class="map_option" min="0" max="1" step="0.05" value="0.5" id="shape_settings_stroke_opacity" />
</td>
<td class="rlabel">
Fill opacity
</td>
<td>
<input type="number" class="map_option" min="0" max="1" step="0.05" value="0.5" id="shape_settings_fill_opacity" />
</td>
</tr>
<tr>
<td class="rlabel">
Stroke weight
</td>
<td>
<input type="number" class="map_option" min="1" max="10" value="3" id="shape_settings_stroke_weight" />
</td>
</tr>
<tr class="section">
<td colspan="4">
Marker and infowindow
</td>
</tr>
<tr>
<td class="rlabel">
Marker title<br/><i>visible as a tooltip on hover</i>
</td>
<td colspan="2">
<input type="text" style="width: 100%" class="map_option" id="marker_settings_title" />
</td>
</tr>
<tr>
<td class="rlabel">
Marker URL<br/><i>http://...</i><br/><i>empty = default Pin Marker</i>
</td>
<td colspan="2">
<input type="text" style="width: 100%" class="map_option" id="marker_settings_icon" />
</td>
</tr>
<tr>
<td class="rlabel">
Infowindow text
</td>
<td colspan="3">
<textarea name="part-<?php echo $this->id; ?>_partname_<?php echo $infowindow_text_in_part; ?>" id="map_infowindow_textarea"></textarea>
</td>
</tr>
</tbody>
</table>
<input type="button" class="button" id="path_store_button" value="Save polygon shape now" style="display:inline-block;"/>
<input type="button" class="button" id="map_settings_store_button" value="Save map settings now" style="display:inline-block;"/>
<input type="button" class="button" id="infowindow_store_button" value="Save infowindow text now" style="display:inline-block;"/>
<textarea name="part-<?php echo $this->id; ?>_partname_<?php echo $path_in_part; ?>" id="path_textarea" style="display: none;"></textarea>
<textarea name="part-<?php echo $this->id; ?>_partname_<?php echo $settings_in_part; ?>" style="height: 400px;display: none;" id="map_settings_textarea"></textarea>
</div>
<?php endif; ?>
<?php
/**
* I'D BE VERY HAPPY IF YOU DIDN'T DELETE THE LINK BELOW
* But of course you can :)
*/
?>
<small> Google Map Generator - <a href="http://marekmurawski.pl">Marek Murawski</a></small>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment