[FIX][IMP] Make partner maps work properly

[FIX] website*: unfuck buggy controllers
[IMP] website*: display GoogleMap in a human-usable interface
[IMP] website_google_map: large module cleaning
      - There is now only one controller, data is sent once for all!
      - Map is now fully resizable in its hosting template
      - HTML/CSS cleaning
      - JavaScript is now human-readable ;-)
This commit is contained in:
Richard Mathot 2014-06-03 16:34:30 +02:00
parent c1ae43c807
commit f04e6de2e7
14 changed files with 231 additions and 194 deletions

View File

@ -146,7 +146,7 @@ class WebsiteCrmPartnerAssign(http.Controller):
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/partners/<partner_id>'], type='http', auth="public", website=True)
def partners_detail(self, partner_id, partner_name='', **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
mo = re.search('([-0-9]+)$', str(partner_id))
current_grade, current_country = None, None
grade_id = post.get('grade_id')
country_id = post.get('country_id')

View File

@ -115,10 +115,24 @@
<template id="ref_country" inherit_id="website_crm_partner_assign.index" optional="enabled" name="Left World Map">
<xpath expr="//ul[@id='reseller_countries']" position="after">
<h3>World Map</h3>
<!-- modal for large map -->
<div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">World Map</h4>
</div>
<iframe t-attf-src="/google_map/?width=898&amp;height=485&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners/"
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
</div>
</div>
</div>
<!-- modal end -->
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".partner_map_modal"><span class="fa fa-external-link" /></button></h3>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners"
style="width:260px; height:260px; border:0; padding:0; margin:0;"></iframe>
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners/"
style="width:260px; height:240px; border:0; padding:0; margin:0;"></iframe>
</ul>
</xpath>
</template>

View File

@ -84,7 +84,7 @@ class WebsiteCustomer(http.Controller):
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/customers/<partner_id>'], type='http', auth="public", website=True)
def partners_detail(self, partner_id, **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
mo = re.search('([-0-9]+)$', str(partner_id))
if mo:
partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)

View File

@ -66,11 +66,27 @@
</template>
<!-- Option: left column: World Map -->
<template id="opt_country" inherit_id="website_customer.index" optional="disabled" name="Show Map">
<template id="opt_country" inherit_id="website_customer.index" optional="enabled" name="Show Map">
<xpath expr="//div[@id='ref_left_column']" position="inside">
<iframe t-attf-src="/google_map/?partner_ids=#{ google_map_partner_ids }&amp;partner_url=/customers/&amp;output=embed"
style="width:100%; border:0; padding:0; margin:0;"></iframe>
<!-- modal for large map -->
<div class="modal fade customer_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">World Map</h4>
</div>
<iframe t-attf-src="/google_map/?width=898&amp;height=485&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/customers/"
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
</div>
</div>
</div>
<!-- modal end -->
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".customer_map_modal"><span class="fa fa-external-link" /></button></h3>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/customers/"
style="width:260px; height:240px; border:0; padding:0; margin:0;"></iframe>
</ul>
</xpath>
</template>

View File

@ -1,2 +1 @@
import controllers
import models

View File

@ -1,3 +1,2 @@
import main
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,48 +1,64 @@
# -*- coding: utf-8 -*-
import openerp
import json
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
from datetime import datetime
class google_map(http.Controller):
'''
This class generates on-the-fly partner maps that can be reused in every
website page. To do so, just use an ``<iframe ...>`` whose ``src``
attribute points to ``/google_map`` (this controller generates a complete
HTML5 page).
URL query parameters:
- ``partner_ids``: a comma-separated list of ids (partners to be shown)
- ``partner_url``: the base-url to display the partner
(eg: if ``partner_url`` is ``/partners/``, when the user will click on
a partner on the map, it will be redirected to <myodoo>.com/partners/<id>)
In order to resize the map, simply resize the ``iframe`` with CSS
directives ``width`` and ``height``.
'''
@http.route(['/google_map'], type='http', auth="public", website=True)
def google_map(self, *arg, **post):
cr, uid, context = request.cr, request.uid, request.context
partner_obj = request.registry['res.partner']
# filter real ints from query parameters and build a domain
clean_ids = []
for s in post.get('partner_ids', "").split(","):
try:
i = int(s)
clean_ids.append(i)
except ValueError:
pass
# search for partners that can be displayed on a map
domain = [("id", "in", clean_ids), ('website_published', '=', True), ('is_company', '=', True)]
partners_ids = partner_obj.search(cr, SUPERUSER_ID, domain, context=context)
# browse and format data
partner_data = {
"counter": len(partners_ids),
"partners": []
}
request.context.update({'show_address': True})
for partner in partner_obj.browse(cr, SUPERUSER_ID, partners_ids, context=context):
partner_data["partners"].append({
'id': partner.id,
'name': partner.name,
'address': '\n'.join(partner.name_get()[0][1].split('\n')[1:]),
'latitude': partner.partner_latitude,
'longitude': partner.partner_longitude,
})
# generate the map
values = {
'partner_ids': post.get('partner_ids', ""),
'width': post.get('width', 900),
'height': post.get('height', 460),
'partner_url': post.get('partner_url'),
'partner_data': json.dumps(partner_data)
}
return request.website.render("website_google_map.google_map", values)
@http.route(['/google_map/partners.json'], type='http', auth="public", website=True)
def google_map_data(self, *arg, **post):
partner_obj = request.registry['res.partner']
domain = [("id", "in", [int(p) for p in post.get('partner_ids', "").split(",") if p])]
domain_public = domain + [('website_published', '=', True)]
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID,
domain_public, context=request.context)
return partner_obj.google_map_json(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
@http.route(['/google_map/set_partner_position'], type='http', methods=['POST'], auth="public", website=True)
def google_map_set_partner_position(self, *arg, **post):
partner_obj = request.registry['res.partner']
partner_id = post.get('partner_id') and int(post['partner_id'])
latitude = post.get('latitude') and float(post['latitude'])
longitude = post.get('longitude') and float(post['longitude'])
values = {
'partner_latitude': latitude,
'partner_longitude': longitude,
'date_localization': datetime.now().strftime('%Y-%m-%d'),
}
partner_obj.write(request.cr, openerp.SUPERUSER_ID, [partner_id], values,
request.context)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1 +0,0 @@
import res_partner

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv
import simplejson
import werkzeug.wrappers
class res_partner(osv.Model):
_inherit = 'res.partner'
def google_map_json(self, cr, uid, ids, context=None):
data = {
"counter": len(ids),
"partners": []
}
for partner in self.pool.get('res.partner').browse(cr, uid, ids, context={'show_address': True}):
data["partners"].append({
'id': partner.id,
'name': partner.name,
'address': '\n'.join(partner.name_get()[0][1].split('\n')[1:]),
'latitude': partner.partner_latitude,
'longitude': partner.partner_longitude,
})
mime = 'application/json'
body = "var data = " + "}, \n{".join(simplejson.dumps(data).split("}, {"))
return werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])

View File

@ -0,0 +1,28 @@
html {
height: 100%;
}
body {
margin: 0;
padding: 0;
height: 100%;
}
#odoo-google-map {
width: 100%;
height: 100%;
}
.marker {
font-size: 13px !important;
}
.marker a {
text-decoration: none;
}
.marker pre {
margin-top: 0;
margin-bottom: 0;
font-family: sans-serif !important;
}

View File

@ -1,84 +1,83 @@
function initialize(pt) {
var center = new google.maps.LatLng(10.91, 5.38);
var Geocoder = new google.maps.Geocoder();
function initialize_map() {
'use strict';
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 1,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
// MAP CONFIG AND LOADING
var map = new google.maps.Map(document.getElementById('odoo-google-map'), {
zoom: 1,
center: {lat: 0.0, lng: 0.0},
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infoWindow = new google.maps.InfoWindow();
// ENABLE ADRESS GEOCODING
var Geocoder = new google.maps.Geocoder();
google.maps.event.addListener(map, 'click', function() {
infoWindow.close();
});
// INFO BUBBLES
var infoWindow = new google.maps.InfoWindow();
var partners = new google.maps.MarkerImage('/website_google_map/static/src/img/partners.png', new google.maps.Size(25, 25));
var partner_url = document.body.getAttribute('data-partner-url') || '';
var markers = [];
var partners = new google.maps.MarkerImage("/website_google_map/static/src/img/partners.png",new google.maps.Size(25, 25));
google.maps.event.addListener(map, 'click', function() {
infoWindow.close();
});
var markers = [];
// Display the bubble once clicked
var onMarkerClick = function() {
var marker = this;
var p = marker.partner;
infoWindow.setContent(
'<div class="marker">'+
(partner_url.length ? '<a target="_top" href="'+partner_url+p.id+'"><b>'+p.name +'</b></a>' : '<b>'+p.name+'</b>' )+
(p.type ? ' <b>' + p.type + '</b>' : '')+
' <pre>' + p.address + '</pre>'+
'</div>'
);
infoWindow.open(map, marker);
};
var onMarkerClick = function() {
var marker = this;
var p = marker.partner;
infoWindow.setContent(
'<div class="marker">'+
(partner_url.length ? '<a target="_top" href="'+partner_url+p.id+'"><b>'+p.name +'</b></a>' : '<b>'+p.name+'</b>' )+ '<br/>'+
(p.type ? ' <b>' + p.type + '</b>' : '')+
' <pre>' + p.address + '</pre>'+
'</div>'
);
infoWindow.open(map, marker);
};
var set_marker = function(partner) {
if (!partner.latitude && !partner.longitude) {
Geocoder.geocode( { 'address': partner.address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var location = results[0].geometry.location;
$.post("/google_map/set_partner_position", {
'partner_id': partner.id,
'latitude': location.ob,
'longitude': location.pb
});
partner.latitude = location.ob;
partner.longitude = location.pb;
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
partner: partner,
map: map,
icon: partners,
position: location
});
google.maps.event.addListener(marker, 'click', onMarkerClick);
markers.push(marker);
// Create a bubble for a partner
var set_marker = function(partner) {
// If no lat & long, geocode adress
// TODO: a server cronjob that will store these coordinates in database instead of resolving them on-the-fly
if (!partner.latitude && !partner.longitude) {
Geocoder.geocode({'address': partner.address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
var location = results[0].geometry.location;
partner.latitude = location.ob;
partner.longitude = location.pb;
var marker = new google.maps.Marker({
partner: partner,
map: map,
icon: partners,
position: location
});
google.maps.event.addListener(marker, 'click', onMarkerClick);
markers.push(marker);
} else {
console.debug('Geocode was not successful for the following reason: ' + status);
}
});
} else {
console.debug('Geocode was not successful for the following reason: ' + status);
var latLng = new google.maps.LatLng(partner.latitude, partner.longitude);
var marker = new google.maps.Marker({
partner: partner,
icon: partners,
map: map,
position: latLng
});
google.maps.event.addListener(marker, 'click', onMarkerClick);
markers.push(marker);
}
});
};
} else {
var latLng = new google.maps.LatLng(partner.latitude, partner.longitude);
var marker = new google.maps.Marker({
partner: partner,
icon: partners,
position: latLng
});
google.maps.event.addListener(marker, 'click', onMarkerClick);
markers.push(marker);
// Create the markers and cluster them on the map
if (odoo_partner_data){ /* odoo_partner_data special variable should have been defined in google_map.xml */
for (var i = 0; i < odoo_partner_data.counter; i++) {
set_marker(odoo_partner_data.partners[i]);
}
var markerCluster = new MarkerClusterer(map, markers);
}
};
if (data)
for (var i = 0; i < data.counter; i++) {
set_marker(data.partners[i]);
}
var markerCluster = new MarkerClusterer(map, markers);
}
google.maps.event.addDomListener(window, 'load', initialize);
// Initialize map once the DOM has been loaded
google.maps.event.addDomListener(window, 'load', initialize_map);

View File

@ -1,42 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="google_map">
<data>
<template id="google_map">
&lt;!DOCTYPE html&gt;
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>World wide map</title>
<style type="text/css">
body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, sans-serif !important;
}
.marker {
font-size: 12px !important;
}
.marker b {
font-weight: 500;
}
.marker pre {
font-size: 10px !important;
}
</style>
<script>var partner_url = '<t t-raw="partner_url or ''"/>';</script>
<script src="//maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" t-attf-src="/google_map/partners.json?partner_ids=#{ partner_ids }"></script>
<script type="text/javascript" src="/website_google_map/static/src/js/markerclusterer_compiled.js"></script>
<script src="//code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="/website_google_map/static/src/js/google_map.js"></script>
</head>
<body>
<div id="map" t-attf-style="width: #{ width }px; height: #{ height }px"></div>
</body>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<title>World Map</title>
<link rel="stylesheet" type="text/css" href="/website_google_map/static/src/css/google-map.css" />
</head>
<body t-att-data-partner-url="partner_url or ''">
<script>
var odoo_partner_data = <t t-raw="partner_data"/>;
</script>
<div id="odoo-google-map"></div>
<script src="//maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="/website_google_map/static/src/js/markerclusterer_compiled.js"></script>
<script type="text/javascript" src="/website_google_map/static/src/js/google_map.js"></script>
</body>
</html>
</template>
</data>
</template>
</data>
</openerp>

View File

@ -105,7 +105,7 @@ class WebsiteMembership(http.Controller):
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/members/<partner_id>'], type='http', auth="public", website=True)
def partners_detail(self, partner_id, **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
mo = re.search('([-0-9]+)$', str(partner_id))
if mo:
partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
@ -113,4 +113,4 @@ class WebsiteMembership(http.Controller):
values = {}
values['main_object'] = values['partner'] = partner
return request.website.render("website_membership.partner", values)
return self.customers(**post)
return self.members(**post)

View File

@ -28,7 +28,7 @@
<div class="container">
<div class="row">
<div class="col-md-4 mb32" id="left_column">
<div class="col-md-3 mb32" id="left_column">
<ul class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Associations</h3></li>
<li t-att-class="'' if membership else 'active'"><a href="/members">All</a></li>
@ -85,7 +85,7 @@
<template id="opt_index_country" name="Location"
optional="enabled" inherit_id="website_membership.index">
<xpath expr="//div[@id='left_column']/ul[last()]" position="after">
<xpath expr="//div[@id='left_column']/ul[1]" position="after">
<ul class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Location</h3></li>
<t t-foreach="countries">
@ -102,13 +102,25 @@
<!-- Option: index: Left Google Map -->
<template id="opt_index_google_map" name="Left World Map"
optional="enabled" inherit_id="website_membership.index">
<xpath expr="//div[@id='left_column']/ul[1]" position="before">
<ul class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>World Map</h3></li>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=320&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/members"
style="width:320px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul>
<xpath expr="//div[@id='left_column']/ul[last()]" position="after">
<!-- modal for large map -->
<div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">World Map</h4>
</div>
<iframe t-attf-src="/google_map/?width=898&amp;height=485&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/members/"
style="width:898px; height:485px; border:0; padding:0; margin:0;"></iframe>
</div>
</div>
</div>
<!-- modal end -->
<h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".partner_map_modal"><span class="fa fa-external-link" /></button></h3>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/members/"
style="width:260px; height:240px; border:0; padding:0; margin:0;"></iframe>
</ul>
</xpath>
</template>