Add social map, requires to add geolocation extra fields BT#15176

$_configuration['allow_social_map_fields'] = ['fields' => ['terms_villedustage', 'terms_ville']];
pull/2829/head
Julio Montoya 7 years ago
parent f990b59465
commit 73d802a6ad
  1. 8
      main/inc/lib/extra_field.lib.php
  2. 16
      main/inc/lib/extra_field_value.lib.php
  3. 1315
      main/inc/lib/javascript/map/markerclusterer.js
  4. 28
      main/inc/lib/javascript/map/oms.min.js
  5. 3
      main/install/configuration.dist.php
  6. 134
      main/social/map.php
  7. 92
      main/template/default/social/map.tpl

@ -1137,7 +1137,6 @@ class ExtraField extends Model
}
break;
case self::FIELD_TYPE_TEXTAREA:
$form->addHtmlEditor(
'extra_'.$field_details['variable'],
$field_details['display_text'],
@ -1698,6 +1697,13 @@ class ExtraField extends Model
$field_details['display_text'],
['id' => 'extra_'.$field_details['variable']]
);
$form->addHidden(
'extra_'.$field_details['variable'].'_coordinates',
'',
['id' => 'extra_'.$field_details['variable'].'_coordinates']
);
$form->applyFilter('extra_'.$field_details['variable'], 'stripslashes');
$form->applyFilter('extra_'.$field_details['variable'], 'trim');
if ($freezeElement) {

@ -143,6 +143,20 @@ class ExtraFieldValue extends Model
$dirPermissions = api_get_permissions_for_new_directories();
switch ($extraFieldInfo['field_type']) {
case ExtraField::FIELD_TYPE_GEOLOCALIZATION:
if (!empty($value)) {
if (isset($params['extra_'.$extraFieldInfo['variable'].'_coordinates'])) {
$value = $value.'::'.$params['extra_'.$extraFieldInfo['variable'].'_coordinates'];
}
$newParams = [
'item_id' => $params['item_id'],
'field_id' => $extraFieldInfo['id'],
'value' => $value,
'comment' => $comment,
];
self::save($newParams, $showQuery);
}
break;
case ExtraField::FIELD_TYPE_TAG:
if ($type == EntityExtraField::USER_FIELD_TYPE) {
UserManager::delete_user_tags(
@ -170,9 +184,7 @@ class ExtraFieldValue extends Model
foreach ($currentTags as $extraFieldtag) {
$em->remove($extraFieldtag);
}
$em->flush();
$tagValues = is_array($value) ? $value : [$value];
$tags = [];

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
/*
OverlappingMarkerSpiderfier
https://github.com/jawj/OverlappingMarkerSpiderfier
Copyright (c) 2011 - 2017 George MacKerron
Released under the MIT licence: http://opensource.org/licenses/mit-license
Note: The Google Maps API v3 must be included *before* this code
*/
(function(){var m,t,w,y,u,z={}.hasOwnProperty,A=[].slice;this.OverlappingMarkerSpiderfier=function(){function r(a,d){var b,f,e;this.map=a;null==d&&(d={});null==this.constructor.N&&(this.constructor.N=!0,h=google.maps,l=h.event,p=h.MapTypeId,c.keepSpiderfied=!1,c.ignoreMapClick=!1,c.markersWontHide=!1,c.markersWontMove=!1,c.basicFormatEvents=!1,c.nearbyDistance=20,c.circleSpiralSwitchover=9,c.circleFootSeparation=23,c.circleStartAngle=x/12,c.spiralFootSeparation=26,c.spiralLengthStart=11,c.spiralLengthFactor=
4,c.spiderfiedZIndex=h.Marker.MAX_ZINDEX+2E4,c.highlightedLegZIndex=h.Marker.MAX_ZINDEX+1E4,c.usualLegZIndex=h.Marker.MAX_ZINDEX+1,c.legWeight=1.5,c.legColors={usual:{},highlighted:{}},e=c.legColors.usual,f=c.legColors.highlighted,e[p.HYBRID]=e[p.SATELLITE]="#fff",f[p.HYBRID]=f[p.SATELLITE]="#f00",e[p.TERRAIN]=e[p.ROADMAP]="#444",f[p.TERRAIN]=f[p.ROADMAP]="#f00",this.constructor.j=function(a){return this.setMap(a)},this.constructor.j.prototype=new h.OverlayView,this.constructor.j.prototype.draw=function(){});
for(b in d)z.call(d,b)&&(f=d[b],this[b]=f);this.g=new this.constructor.j(this.map);this.C();this.c={};this.B=this.l=null;this.addListener("click",function(a,b){return l.trigger(a,"spider_click",b)});this.addListener("format",function(a,b){return l.trigger(a,"spider_format",b)});this.ignoreMapClick||l.addListener(this.map,"click",function(a){return function(){return a.unspiderfy()}}(this));l.addListener(this.map,"maptypeid_changed",function(a){return function(){return a.unspiderfy()}}(this));l.addListener(this.map,
"zoom_changed",function(a){return function(){a.unspiderfy();if(!a.basicFormatEvents)return a.h()}}(this))}var l,h,m,v,p,c,t,x,u;c=r.prototype;t=[r,c];m=0;for(v=t.length;m<v;m++)u=t[m],u.VERSION="1.0.3";x=2*Math.PI;h=l=p=null;r.markerStatus={SPIDERFIED:"SPIDERFIED",SPIDERFIABLE:"SPIDERFIABLE",UNSPIDERFIABLE:"UNSPIDERFIABLE",UNSPIDERFIED:"UNSPIDERFIED"};c.C=function(){this.a=[];this.s=[]};c.addMarker=function(a,d){a.setMap(this.map);return this.trackMarker(a,d)};c.trackMarker=function(a,d){var b;if(null!=
a._oms)return this;a._oms=!0;b=[l.addListener(a,"click",function(b){return function(d){return b.V(a,d)}}(this))];this.markersWontHide||b.push(l.addListener(a,"visible_changed",function(b){return function(){return b.D(a,!1)}}(this)));this.markersWontMove||b.push(l.addListener(a,"position_changed",function(b){return function(){return b.D(a,!0)}}(this)));null!=d&&b.push(l.addListener(a,"spider_click",d));this.s.push(b);this.a.push(a);this.basicFormatEvents?this.trigger("format",a,this.constructor.markerStatus.UNSPIDERFIED):
(this.trigger("format",a,this.constructor.markerStatus.UNSPIDERFIABLE),this.h());return this};c.D=function(a,d){if(!this.J&&!this.K)return null==a._omsData||!d&&a.getVisible()||this.unspiderfy(d?a:null),this.h()};c.getMarkers=function(){return this.a.slice(0)};c.removeMarker=function(a){this.forgetMarker(a);return a.setMap(null)};c.forgetMarker=function(a){var d,b,f,e,g;null!=a._omsData&&this.unspiderfy();d=this.A(this.a,a);if(0>d)return this;g=this.s.splice(d,1)[0];b=0;for(f=g.length;b<f;b++)e=g[b],
l.removeListener(e);delete a._oms;this.a.splice(d,1);this.h();return this};c.removeAllMarkers=c.clearMarkers=function(){var a,d,b,f;f=this.getMarkers();this.forgetAllMarkers();a=0;for(d=f.length;a<d;a++)b=f[a],b.setMap(null);return this};c.forgetAllMarkers=function(){var a,d,b,f,e,g,c,q;this.unspiderfy();q=this.a;a=d=0;for(b=q.length;d<b;a=++d){g=q[a];e=this.s[a];c=0;for(a=e.length;c<a;c++)f=e[c],l.removeListener(f);delete g._oms}this.C();return this};c.addListener=function(a,d){var b;(null!=(b=this.c)[a]?
b[a]:b[a]=[]).push(d);return this};c.removeListener=function(a,d){var b;b=this.A(this.c[a],d);0>b||this.c[a].splice(b,1);return this};c.clearListeners=function(a){this.c[a]=[];return this};c.trigger=function(){var a,d,b,f,e,g;d=arguments[0];a=2<=arguments.length?A.call(arguments,1):[];d=null!=(b=this.c[d])?b:[];g=[];f=0;for(e=d.length;f<e;f++)b=d[f],g.push(b.apply(null,a));return g};c.L=function(a,d){var b,f,e,g,c;g=this.circleFootSeparation*(2+a)/x;f=x/a;c=[];for(b=e=0;0<=a?e<a:e>a;b=0<=a?++e:--e)b=
this.circleStartAngle+b*f,c.push(new h.Point(d.x+g*Math.cos(b),d.y+g*Math.sin(b)));return c};c.M=function(a,d){var b,f,e,c,k;c=this.spiralLengthStart;b=0;k=[];for(f=e=0;0<=a?e<a:e>a;f=0<=a?++e:--e)b+=this.spiralFootSeparation/c+5E-4*f,f=new h.Point(d.x+c*Math.cos(b),d.y+c*Math.sin(b)),c+=x*this.spiralLengthFactor/b,k.push(f);return k};c.V=function(a,d){var b,f,e,c,k,q,n,l,h;(q=null!=a._omsData)&&this.keepSpiderfied||this.unspiderfy();if(q||this.map.getStreetView().getVisible()||"GoogleEarthAPI"===
this.map.getMapTypeId())return this.trigger("click",a,d);q=[];n=[];b=this.nearbyDistance;l=b*b;k=this.f(a.position);h=this.a;b=0;for(f=h.length;b<f;b++)e=h[b],null!=e.map&&e.getVisible()&&(c=this.f(e.position),this.i(c,k)<l?q.push({R:e,G:c}):n.push(e));return 1===q.length?this.trigger("click",a,d):this.W(q,n)};c.markersNearMarker=function(a,d){var b,f,e,c,k,q,n,l,h,m;null==d&&(d=!1);if(null==this.g.getProjection())throw"Must wait for 'idle' event on map before calling markersNearMarker";b=this.nearbyDistance;
n=b*b;k=this.f(a.position);q=[];l=this.a;b=0;for(f=l.length;b<f&&!(e=l[b],e!==a&&null!=e.map&&e.getVisible()&&(c=this.f(null!=(h=null!=(m=e._omsData)?m.v:void 0)?h:e.position),this.i(c,k)<n&&(q.push(e),d)));b++);return q};c.F=function(){var a,d,b,f,e,c,k,l,n,h,m;if(null==this.g.getProjection())throw"Must wait for 'idle' event on map before calling markersNearAnyOtherMarker";n=this.nearbyDistance;n*=n;var p;e=this.a;p=[];h=0;for(d=e.length;h<d;h++)f=e[h],p.push({H:this.f(null!=(a=null!=(b=f._omsData)?
b.v:void 0)?a:f.position),b:!1});h=this.a;a=b=0;for(f=h.length;b<f;a=++b)if(d=h[a],null!=d.getMap()&&d.getVisible()&&(c=p[a],!c.b))for(m=this.a,d=l=0,e=m.length;l<e;d=++l)if(k=m[d],d!==a&&null!=k.getMap()&&k.getVisible()&&(k=p[d],(!(d<a)||k.b)&&this.i(c.H,k.H)<n)){c.b=k.b=!0;break}return p};c.markersNearAnyOtherMarker=function(){var a,d,b,c,e,g,k;e=this.F();g=this.a;k=[];a=d=0;for(b=g.length;d<b;a=++d)c=g[a],e[a].b&&k.push(c);return k};c.setImmediate=function(a){return window.setTimeout(a,0)};c.h=
function(){if(!this.basicFormatEvents&&null==this.l)return this.l=this.setImmediate(function(a){return function(){a.l=null;return null!=a.g.getProjection()?a.w():null!=a.B?void 0:a.B=l.addListenerOnce(a.map,"idle",function(){return a.w()})}}(this))};c.w=function(){var a,d,b,c,e,g,k;if(this.basicFormatEvents){e=[];d=0;for(b=markers.length;d<b;d++)c=markers[d],a=null!=c._omsData?"SPIDERFIED":"UNSPIDERFIED",e.push(this.trigger("format",c,this.constructor.markerStatus[a]));return e}e=this.F();g=this.a;
k=[];a=b=0;for(d=g.length;b<d;a=++b)c=g[a],a=null!=c._omsData?"SPIDERFIED":e[a].b?"SPIDERFIABLE":"UNSPIDERFIABLE",k.push(this.trigger("format",c,this.constructor.markerStatus[a]));return k};c.P=function(a){return{m:function(d){return function(){return a._omsData.o.setOptions({strokeColor:d.legColors.highlighted[d.map.mapTypeId],zIndex:d.highlightedLegZIndex})}}(this),u:function(d){return function(){return a._omsData.o.setOptions({strokeColor:d.legColors.usual[d.map.mapTypeId],zIndex:d.usualLegZIndex})}}(this)}};
c.W=function(a,d){var b,c,e,g,k,q,n,m,p,r;this.J=!0;r=a.length;b=this.T(function(){var b,d,c;c=[];b=0;for(d=a.length;b<d;b++)m=a[b],c.push(m.G);return c}());g=r>=this.circleSpiralSwitchover?this.M(r,b).reverse():this.L(r,b);b=function(){var b,d,f;f=[];b=0;for(d=g.length;b<d;b++)e=g[b],c=this.U(e),p=this.S(a,function(a){return function(b){return a.i(b.G,e)}}(this)),n=p.R,q=new h.Polyline({map:this.map,path:[n.position,c],strokeColor:this.legColors.usual[this.map.mapTypeId],strokeWeight:this.legWeight,
zIndex:this.usualLegZIndex}),n._omsData={v:n.getPosition(),X:n.getZIndex(),o:q},this.legColors.highlighted[this.map.mapTypeId]!==this.legColors.usual[this.map.mapTypeId]&&(k=this.P(n),n._omsData.O={m:l.addListener(n,"mouseover",k.m),u:l.addListener(n,"mouseout",k.u)}),this.trigger("format",n,this.constructor.markerStatus.SPIDERFIED),n.setPosition(c),n.setZIndex(Math.round(this.spiderfiedZIndex+e.y)),f.push(n);return f}.call(this);delete this.J;this.I=!0;return this.trigger("spiderfy",b,d)};c.unspiderfy=
function(a){var d,b,c,e,g,k,h;null==a&&(a=null);if(null==this.I)return this;this.K=!0;h=[];g=[];k=this.a;d=0;for(b=k.length;d<b;d++)e=k[d],null!=e._omsData?(e._omsData.o.setMap(null),e!==a&&e.setPosition(e._omsData.v),e.setZIndex(e._omsData.X),c=e._omsData.O,null!=c&&(l.removeListener(c.m),l.removeListener(c.u)),delete e._omsData,e!==a&&(c=this.basicFormatEvents?"UNSPIDERFIED":"SPIDERFIABLE",this.trigger("format",e,this.constructor.markerStatus[c])),h.push(e)):g.push(e);delete this.K;delete this.I;
this.trigger("unspiderfy",h,g);return this};c.i=function(a,d){var b,c;b=a.x-d.x;c=a.y-d.y;return b*b+c*c};c.T=function(a){var c,b,f,e,g;c=e=g=0;for(b=a.length;c<b;c++)f=a[c],e+=f.x,g+=f.y;a=a.length;return new h.Point(e/a,g/a)};c.f=function(a){return this.g.getProjection().fromLatLngToDivPixel(a)};c.U=function(a){return this.g.getProjection().fromDivPixelToLatLng(a)};c.S=function(a,c){var b,d,e,g,k,h;e=k=0;for(h=a.length;k<h;e=++k)if(g=a[e],g=c(g),"undefined"===typeof b||null===b||g<d)d=g,b=e;return a.splice(b,
1)[0]};c.A=function(a,c){var b,d,e,g;if(null!=a.indexOf)return a.indexOf(c);b=d=0;for(e=a.length;d<e;b=++d)if(g=a[b],g===c)return b;return-1};return r}();t=/(\?.*(&|&amp;)|\?)spiderfier_callback=(\w+)/;m=document.currentScript;null==m&&(m=function(){var m,l,h,w,v;h=document.getElementsByTagName("script");v=[];m=0;for(l=h.length;m<l;m++)u=h[m],null!=(w=u.getAttribute("src"))&&w.match(t)&&v.push(u);return v}()[0]);if(null!=m&&(m=null!=(w=m.getAttribute("src"))?null!=(y=w.match(t))?y[3]:void 0:void 0)&&
"function"===typeof window[m])window[m]();"function"===typeof window.spiderfier_callback&&window.spiderfier_callback()}).call(this);
/* Thu 11 May 2017 08:40:57 BST */

@ -1128,6 +1128,9 @@ VALUES (2, 13, 'session_courses_read_only_mode', 'Lock Course In Session', 1, 1,
// Requires new forum_category and forum_post "language" extra fields (multiple select)
//$_configuration['allow_forum_post_revisions'] = false;
// Allow to show users in a map, users need to have a coordinates extra field BT#15176
//$_configuration['allow_social_map_fields'] = ['fields' => ['terms_villedustage', 'terms_ville']];
// ------ Custom DB changes (keep this at the end)
// Add user activation by confirmation email
// This option prevents the new user to login in the platform if your account is not confirmed via email

@ -0,0 +1,134 @@
<?php
/* For licensing terms, see /license.txt */
/**
* @package chamilo.social
*
* @author Julio Montoya <gugli100@gmail.com>
*/
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
api_block_anonymous_users();
$fields = api_get_configuration_value('allow_social_map_fields');
if (!$fields) {
api_not_allowed(true);
}
$fields = isset($fields['fields']) ? $fields['fields'] : '';
if (empty($fields)) {
api_not_allowed(true);
}
$extraField = new ExtraField('user');
$infoStage = $extraField->get_handler_field_info_by_field_variable($fields['0']);
$infoVille = $extraField->get_handler_field_info_by_field_variable($fields['1']);
$tableUser = Database::get_main_table(TABLE_MAIN_USER);
$sql = "SELECT u.id, firstname, lastname, ev.value ville, ev2.value stage
FROM $tableUser u
INNER JOIN extra_field_values ev
ON ev.item_id = u.id
INNER JOIN extra_field_values ev2
ON ev2.item_id = u.id
WHERE
ev.field_id = ".$infoStage['id']." AND
ev2.field_id = ".$infoVille['id']." AND
u.status = ".STUDENT." AND
u.active = 1 AND
(ev.value <> '' OR ev2.value <> '') AND
(ev.value LIKE '%::%' OR ev2.value LIKE '%::%')
";
$cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
$keyDate = 'map_cache_date';
$keyData = 'map_cache_data';
$now = time();
// Refresh cache every day
//$tomorrow = strtotime('+1 day', $now);
$tomorrow = strtotime('+5 minute', $now);
$loadFromDatabase = true;
if ($cacheDriver->contains($keyData) && $cacheDriver->contains($keyDate)) {
$savedDate = $cacheDriver->fetch($keyDate);
$loadFromDatabase = false;
if ($savedDate < $now) {
$loadFromDatabase = true;
}
}
$loadFromDatabase = true;
if ($loadFromDatabase) {
$result = Database::query($sql);
$data = Database::store_result($result, 'ASSOC');
$cacheDriver->save($keyData, $data);
$cacheDriver->save($keyDate, $tomorrow);
} else {
$data = $cacheDriver->fetch($keyData);
}
foreach ($data as &$result) {
$result['complete_name'] = addslashes(api_get_person_name($result['firstname'], $result['lastname']));
$parts = explode('::', $result['ville']);
if (isset($parts[1]) && !empty($parts[1])) {
$parts2 = explode(',', $parts[1]);
$result['ville_lat'] = $parts2[0];
$result['ville_long'] = $parts2[1];
unset($result['ville']);
}
$parts = explode('::', $result['stage']);
if (isset($parts[1]) && !empty($parts[1])) {
$parts2 = explode(',', $parts[1]);
$result['stage_lat'] = $parts2[0];
$result['stage_long'] = $parts2[1];
unset($result['stage']);
}
}
$apiKey = api_get_configuration_value('google_api_key');
$htmlHeadXtra[] = '<script type="text/javascript" src="'.api_get_path(WEB_LIBRARY_JS_PATH).'map/markerclusterer.js"></script>';
$htmlHeadXtra[] = '<script type="text/javascript" src="'.api_get_path(WEB_LIBRARY_JS_PATH).'map/oms.min.js"></script>';
$tpl = new Template(null);
$tpl->assign('url', api_get_path(WEB_CODE_PATH).'social/profile.php');
$tpl->assign(
'image_city',
Display::return_icon(
'red-dot.png',
'',
[],
ICON_SIZE_SMALL,
false,
true
)
);
$tpl->assign(
'image_stage',
Display::return_icon(
'blue-dot.png',
'',
[],
ICON_SIZE_SMALL,
false,
true
)
);
$tpl->assign('places', json_encode($data));
$tpl->assign('api_key', $apiKey);
$tpl->assign('field_1', $infoStage['display_text']);
$tpl->assign('field_2', $infoVille['display_text']);
$layout = $tpl->get_template('social/map.tpl');
$tpl->display($layout);

@ -0,0 +1,92 @@
{% extends 'layout/layout_1_col.tpl'|get_template %}
{% block content %}
<div id="map" style="width:100%; height:600px"></div>
<script>
function start()
{
var options = {
center: new google.maps.LatLng(54.526, 15.255), // "Europe center"
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), options);
var oms = new OverlappingMarkerSpiderfier(map);
var cities = '{{ places }}';
cities = JSON.parse(cities);
var imageCity = {
url: '{{ image_city }}'
}
var stageCity = {
url:'{{ image_stage }}'
}
// Add markers
var markers = [];
if (cities.length) {
for (var i = 0; i < cities.length; i++) {
// Add ville
if ('ville_lat' in cities[i]) {
var markerOptions = {
position: new google.maps.LatLng(cities[i]['ville_lat'], cities[i]['ville_long']),
title: cities[i]['complete_name'],
city: cities[i],
icon: imageCity,
};
var marker = new google.maps.Marker(markerOptions);
markers.push(marker);
oms.addMarker(marker);
}
// Add stage
if ('stage_lat' in cities[i]) {
var markerOptions = {
position: new google.maps.LatLng(cities[i]['stage_lat'], cities[i]['stage_long']),
title: cities[i]['complete_name'],
city: cities[i],
icon: stageCity,
};
var marker = new google.maps.Marker(markerOptions);
markers.push(marker);
oms.addMarker(marker);
}
}
// Enable cluster
var markerClusterer = new MarkerClusterer(map, markers, {
maxZoom: 9, // maxZoom set when clustering will stop
imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'
});
// Auto-boxing
if (markers.length) {
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; ++i) {
bounds.extend(markers[i].position);
}
map.fitBounds(bounds);
}
// window when clicking
var infoWindow = new google.maps.InfoWindow();
oms.addListener('click', function (marker, event) {
infoWindow.setContent('<a href="{{ url }}?u=' + marker.city['id'] + '">' + marker.city['complete_name'] + '</a>');
infoWindow.open(map, marker);
});
google.maps.event.addListener(markerClusterer, 'clusterclick', function (cluster) {
map.fitBounds(cluster.getBounds());
if (map.getZoom() > 14) {
map.setZoom(14);
}
});
}
}
</script>
<script async defer type="text/javascript" src="https://maps.google.com/maps/api/js?key={{ api_key }}&callback=start"></script>
<img src="{{ image_city }}" /> {{ field_1 }} <br />
<img src="{{ image_stage }}" /> {{ field_2 }}
{% endblock %}
Loading…
Cancel
Save