Passed
Push — scrutinizer-code-quality ( 09f5a1...c4c5fb )
by Adam
56:05 queued 14:08
created

jjwg_Maps::getGoogleMapsGeocode()   D

Complexity

Conditions 12
Paths 128

Size

Total Lines 88
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 14.3961
Metric Value
cc 12
eloc 49
nc 128
nop 3
dl 0
loc 88
ccs 35
cts 47
cp 0.7447
crap 14.3961
rs 4.6933

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3 1
if (!defined('sugarEntry') || !sugarEntry)
4
    die('Not A Valid Entry Point');
5
6
// modules/jjwg_Maps/jjwg_Maps.php
7
8 1
require_once('modules/jjwg_Maps/jjwg_Maps_sugar.php');
9 1
require_once('modules/Administration/Administration.php');
10
11
class jjwg_Maps extends jjwg_Maps_sugar {
12
13
    /**
14
     * @var settings array
15
     *
16
     */
17
    var $settings = array(
18
        /**
19
         * 'valid_geocode_modules' defines the valid module names used with geocoding.
20
         * @var array
21
         */
22
        'valid_geocode_modules' => array(
23
            'Accounts', 'Contacts', 'Leads', 'Opportunities', 'Cases', 'Project', 'Meetings', 'Prospects'
24
        ),
25
        /**
26
         * 'valid_geocode_tables' defines the valid table names used with geocoding.
27
         * @var array
28
         */
29
        'valid_geocode_tables' => array(
30
            'accounts', 'contacts', 'leads', 'opportunities', 'cases', 'project', 'meetings', 'prospects'
31
        ),
32
        /**
33
         * 'geocode_modules_to_address_type' defines the modules address types to be used with geocoding.
34
         * Acceptable Values: 'billing', 'shipping', 'primary', 'alt', 'flex_relate', 'custom', 'address'
35
         * @var array
36
         */
37
        'geocode_modules_to_address_type' => array(
38
            'Accounts' => 'billing',
39
            'Contacts' => 'primary',
40
            'Leads' => 'primary',
41
            'Opportunities' => 'billing',
42
            'Cases' => 'billing',
43
            'Project' => 'billing',
44
            'Meetings' => 'flex_relate',
45
            'Prospects' => 'primary',
46
            'Users' => 'address'
47
        ),
48
        /**
49
         * 'geocoding_api_url' sets the URL used for geocoding (Google or Proxy)
50
         * @var string
51
         */
52
        'geocoding_api_url' => 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false',
53
        /**
54
         * 'geocoding_api_secret' sets a secret phrase to be used by a Proxy for hash comparison
55
         * @var string
56
         * 'hash' is added to the URL parameters as a MD5 of the Concatenation of the Address and the Secret
57
         */
58
        'geocoding_api_secret' => '',
59
        /**
60
         * 'geocoding_limit' sets the query limit when selecting records to geocode.
61
         * @var integer
62
         */
63
        'geocoding_limit' => 250,
64
        /**
65
         * 'google_geocoding_limit' sets the request limit when geocoding using the Google Maps API.
66
         * @var integer
67
         */
68
        'google_geocoding_limit' => 100,
69
        /**
70
         * 'allow_approximate_location_type' allows a Geocoding 'location_type' of 'APPROXIMATE' to be considered an 'OK' Status
71
         */
72
        'allow_approximate_location_type' => false,
73
        /**
74
         * 'map_markers_limit' sets the query limit when selecting records to display on a map.
75
         * @var integer
76
         */
77
        'map_markers_limit' => 1000,
78
        /**
79
         * 'map_default_unit_type' sets the default unit measurement type for distance calculations.
80
         * @var string
81
         * Values: 'mi' (miles) or 'km' (kilometers)
82
         */
83
        'map_default_unit_type' => 'mi',
84
        /**
85
         * 'map_default_distance' sets the default distance used for distance based maps.
86
         * @var float
87
         */
88
        'map_default_distance' => 250,
89
        /**
90
         * 'map_duplicate_marker_adjustment' sets an offset to be added to
91
         * longitude and latitude in case of duplicate marker position.
92
         * @var float
93
         */
94
        'map_duplicate_marker_adjustment' => 0.00002,
95
        /**
96
         * 'export_addresses_limit' sets the query limit when selecting records to export.
97
         * @var integer
98
         */
99
        'export_addresses_limit' => 50000,
100
        /**
101
         * 'map_markers_grouping_field' sets the field to be used as the group parameter when display on a map.
102
         * @var array
103
         * Examples: assigned_user_name, industry, status, sales_stage, priority
104
         */
105
        'map_markers_grouping_field' => array(
106
            'Accounts' => 'industry',
107
            'Contacts' => 'assigned_user_name',
108
            'Leads' => 'status',
109
            'Opportunities' => 'sales_stage',
110
            'Cases' => 'priority',
111
            'Project' => 'assigned_user_name',
112
            'Meetings' => 'assigned_user_name',
113
            'Prospects' => 'assigned_user_name',
114
            'Users' => 'user_name'
115
        ),
116
        /**
117
         * 'map_clusterer_grid_size' is used to set the grid size for calculating map clusterers.
118
         * @var integer
119
         */
120
        'map_clusterer_grid_size' => 50,
121
        /**
122
         * 'map_clusterer_max_zoom' is used to set the maximum zoom level at which clustering will not be applied.
123
         * @var integer
124
         */
125
        'map_clusterer_max_zoom' => 14,
126
        /**
127
         * 'map_default_center_latitude' sets the default center latitude position for maps.
128
         * @var float
129
         */
130
        'map_default_center_latitude' => 39.5,
131
        /**
132
         * 'map_default_center_longitude' sets the default center longitude position for maps.
133
         * @var float
134
         */
135
        'map_default_center_longitude' => -99.5,
136
        /**
137
         * 'address_cache_get_enabled' allows for the getAddressCacheGeocode()
138
         * method to retrieve data from cache table: jjwg_Address_Cache
139
         * @var boolean 1/0
140
         */
141
        'address_cache_get_enabled' => true,
142
143
        /**
144
         * 'address_cache_save_enabled' allows for the saveAddressCacheGeocode()
145
         * method to save data to cache table: jjwg_Address_Cache
146
         * @var boolean 1/0
147
         */
148
        'address_cache_save_enabled' => true,
149
150
        /**
151
         * 'logic_hooks_enabled' allows admins to disable the logic hooks functionality
152
         * which is very useful during upgrading processes
153
         */
154
        'logic_hooks_enabled' => false
155
    );
156
157
    /**
158
     * relate_object - related module's object (flex relate field)
159
     * @var object
160
     */
161
    var $relate_object;
162
163
    /**
164
     * jjwg_Address_Cache - Address cache module's object
165
     * @var object
166
     */
167
    var $jjwg_Address_Cache;
168
169
170
    /**
171
     * geocoded_counts - Geocoding totals
172
     * @var array
173
     */
174
    var $geocoded_counts = null;
175
176
    /**
177
     * geocoded_headings - Display headings
178
     * @var array
179
     */
180
    var $geocoded_headings = null;
181
182
    /**
183
     * geocoded_module_totals - Geocoded module totals
184
     * @var array
185
     */
186
    var $geocoded_module_totals = null;
187
188
    /**
189
     * geocoding_results - Google Geocoding API Results
190
     * @var array
191
     */
192
    var $geocoding_results = null;
193
194
    /**
195
     * map_center - Map Center (Related)
196
     * @var array
197
     */
198
    var $map_center = null;
199
200
    /**
201
     * map_markers - Map Marker Data (Display)
202
     * @var array
203
     */
204
    var $map_markers = null;
205
206
    /**
207
     * map_markers_groups - Sets the array of map groups
208
     * @var array
209
     */
210
    var $map_markers_groups = array();
211
212
    /**
213
     * map_markers - Custom Markers Data (jjwg_Markers)
214
     * @var array
215
     */
216
    var $custom_markers = null;
217
218
    /**
219
     * custom_areas - Custom Areas Data (jjwg_Areas)
220
     * @var array
221
     */
222
    var $custom_areas = null;
223
224
225
226
    /**
227
     * Constructor
228
     */
229 46
    function __construct($init=true) {
230
231 46
        parent::__construct();
232
        // Admin Config Setting
233 46
        if($init)$this->configuration();
234 46
    }
235
236
    /**
237
     * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
238
     */
239
    function jjwg_Maps($init=true){
240
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
241
        if(isset($GLOBALS['log'])) {
242
            $GLOBALS['log']->deprecated($deprecatedMessage);
243
        }
244
        else {
245
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
246
        }
247
        self::__construct($init);
248
    }
249
250
251
    /**
252
     * Load Configuration Settings using Administration Module
253
     *
254
     */
255 46
    function configuration() {
256
257
        // Set defaults
258 46
        $GLOBALS['jjwg_config_defaults'] = $this->settings;
259
260
        // Adjust Query Limit
261 46
        if ($GLOBALS['sugar_config']['resource_management']['default_limit'] < 20000) {
262
            $GLOBALS['sugar_config']['resource_management']['default_limit'] = 20000;
263
        }
264
265 46
        $admin = new Administration();
266 46
        $admin->retrieveSettings('jjwg', true);
267 46
        $settings = $admin->settings;
268
269 46
        $rev = array();
270 46
        if (!empty($settings) && count($settings) > 0) {
271 46
            foreach ($settings as $category_name => $value) {
272
273 46
                if (substr($category_name, 0, 5) == 'jjwg_') {
274 18
                    $name = substr($category_name, 5);
275
                    // Set revised settings array
276 46
                    $rev[$name] = $value;
277
                }
278
            }
279
        }
280
281 46
        if (!empty($rev) && count($rev) > 0) {
282
283 18
            foreach ($rev as $name => $value) {
284
285
                // Set geocode_modules_to_address_type
286 18
                if (substr($name, 0, 13) == 'address_type_') {
287
                    $module = substr($name, 13);
288
                    if (in_array($value, array('billing', 'shipping', 'primary', 'alt', 'flex_relate', 'custom', 'address'))) {
289
                        $this->settings['geocode_modules_to_address_type'][$module] = $value;
290
                    }
291
                }
292
293
                // Set map_markers_grouping_field
294 18
                if (substr($name, 0, 15) == 'grouping_field_') {
295
                    $module = substr($name, 15);
296
                    if (!empty($value)) {
297 18
                        $this->settings['map_markers_grouping_field'][$module] = $value;
298
                    }
299
                }
300
301
            }
302
303 18
            if (!empty($rev['valid_geocode_modules'])) {
304
                if (!is_array($rev['valid_geocode_modules'])) {
305
                    $this->settings['valid_geocode_modules'] = preg_split('/[\s,]+/', $rev['valid_geocode_modules']);
306
                }
307
            }
308 18
            if (!empty($rev['valid_geocode_tables'])) {
309
                if (!is_array($rev['valid_geocode_tables'])) {
310
                    $this->settings['valid_geocode_tables'] = preg_split('/[\s,]+/', $rev['valid_geocode_tables']);
311
                }
312
            }
313
314
            // Integer Settings
315 18
            $int_settings = array('geocoding_limit', 'google_geocoding_limit',
316
                'map_markers_limit', 'map_default_distance', 'export_addresses_limit',
317
                'map_clusterer_grid_size', 'map_clusterer_max_zoom');
318 18
            foreach ($int_settings as $setting) {
319 18
                if (isset($rev[$setting]) && is_numeric($rev[$setting])) {
320 18
                    $this->settings[$setting] = (int) $rev[$setting];
321
                }
322
            }
323
324
            // Float/Language Settings
325 18
            if (isset($rev['map_default_unit_type']) && in_array($rev['map_default_unit_type'], array('mi', 'km'))) {
326
                $this->settings['map_default_unit_type'] = $rev['map_default_unit_type'];
327
            }
328 18
            if (isset($rev['map_duplicate_marker_adjustment']) && is_numeric($rev['map_duplicate_marker_adjustment'])) {
329 18
                $this->settings['map_duplicate_marker_adjustment'] = (float) $rev['map_duplicate_marker_adjustment'];
330
            }
331 18
            if (isset($rev['map_default_center_latitude']) && is_numeric($rev['map_default_center_latitude'])) {
332 18
                $this->settings['map_default_center_latitude'] = (float) $rev['map_default_center_latitude'];
333
            }
334 18
            if (isset($rev['map_default_center_longitude']) && is_numeric($rev['map_default_center_longitude'])) {
335 18
                $this->settings['map_default_center_longitude'] = (float) $rev['map_default_center_longitude'];
336
            }
337
338
            // Address Cache Settings: true/false or 1/0
339 18
            $this->settings['address_cache_get_enabled'] = (!empty($rev['address_cache_get_enabled'])) ? true : false;
340 18
            $this->settings['address_cache_save_enabled'] = (!empty($rev['address_cache_save_enabled'])) ? true : false;
341
            // Logic Hooks: true/false or 1/0
342 18
            $this->settings['logic_hooks_enabled'] = (!empty($rev['logic_hooks_enabled'])) ? true : false;
343
            // Allow APPROXIMATE 'location_type'
344 18
            $this->settings['allow_approximate_location_type'] = (!empty($rev['allow_approximate_location_type'])) ? true : false;
345
346
            // Set Geocoding API URL or Proxy URL
347 18
            if (isset($rev['geocoding_api_url'])) {
348 18
                $this->settings['geocoding_api_url'] = $rev['geocoding_api_url'];
349
            }
350
            // Set Google Maps API Secret
351 18
            if (isset($rev['geocoding_api_secret'])) {
352 18
                $this->settings['geocoding_api_secret'] = $rev['geocoding_api_secret'];
353
            }
354
355
        }
356
357
        // Set for Global Use
358 46
        $GLOBALS['jjwg_config'] = $this->settings;
359 46
    }
360
361
    /**
362
     * Save Configuration Settings using Administration Module
363
     *
364
     * @param $data array of post data
365
     */
366 1
    function saveConfiguration($data = array()) {
367
368 1
        $admin = new Administration();
369
        //$admin->retrieveSettings('jjwg', true);
370
        //$settings = $admin->settings;
371 1
        $category = 'jjwg';
372
373 1
        if (!empty($data) && count($data) > 0) {
374
375 1
            if (isset($data['valid_geocode_modules'])) {
376
                if (is_array($data['valid_geocode_modules'])) {
377
                    $data['valid_geocode_modules'] = join(', ', $data['valid_geocode_modules']);
378
                }
379
                $admin->saveSetting($category, 'valid_geocode_modules', $data['valid_geocode_modules']);
380
            }
381 1
            if (isset($data['valid_geocode_tables'])) {
382
                if (is_array($data['valid_geocode_tables'])) {
383
                    $data['valid_geocode_tables'] = join(', ', $data['valid_geocode_tables']);
384
                }
385
                $admin->saveSetting($category, 'valid_geocode_tables', $data['valid_geocode_tables']);
386
            }
387
388 1
            foreach ($data as $name => $value) {
389
390
                // Trim white space on each
391 1
                $value = trim($value);
392
393
                // Set geocode_modules_to_address_type
394 1
                if (substr($name, 0, 13) == 'address_type_') {
395
                    $module = substr($name, 13);
396
                    if (in_array($module, $this->settings['valid_geocode_modules'])) {
397
                        if (in_array($value, array('billing', 'shipping', 'primary', 'alt', 'flex_relate', 'custom', 'address'))) {
398
                            $admin->saveSetting($category, $name, $value);
399
                        }
400
                    }
401
                }
402
403
                // Set map_markers_grouping_field
404 1
                if (substr($name, 0, 15) == 'grouping_field_') {
405
                    $module = substr($name, 15);
406
                    if (in_array($module, $this->settings['valid_geocode_modules'])) {
407
                        if (!empty($value)) {
408 1
                            $admin->saveSetting($category, $name, $value);
409
                        }
410
                    }
411
                }
412
413
            }
414
415
            // Integer Settings
416 1
            $int_settings = array('geocoding_limit', 'google_geocoding_limit',
417
                'map_markers_limit', 'map_default_distance', 'export_addresses_limit',
418
                'map_clusterer_grid_size', 'map_clusterer_max_zoom',
419
                'address_cache_get_enabled', 'address_cache_save_enabled',
420
                'logic_hooks_enabled', 'allow_approximate_location_type');
421 1
            foreach ($int_settings as $setting) {
422 1
                if (isset($data[$setting]) && is_numeric(trim($data[$setting]))) {
423 1
                    $admin->saveSetting($category, $setting, (int) trim($data[$setting]));
424
                }
425
            }
426
427
            // Float/Language Settings
428 1
            if (isset($data['map_default_unit_type']) && in_array(trim($data['map_default_unit_type']), array('mi', 'km'))) {
429
                $admin->saveSetting($category, 'map_default_unit_type', trim($data['map_default_unit_type']));
430
            }
431 1
            if (empty($data['map_duplicate_marker_adjustment'])) $data['map_duplicate_marker_adjustment'] = 0.00002;
432 1
            if (isset($data['map_duplicate_marker_adjustment']) && is_numeric(trim($data['map_duplicate_marker_adjustment']))) {
433 1
                $admin->saveSetting($category, 'map_duplicate_marker_adjustment', (float) trim($data['map_duplicate_marker_adjustment']));
434
            }
435 1
            if (!$this->is_valid_lat($data['map_default_center_latitude'])) $data['map_default_center_latitude'] = 39.5;
436 1
            if (isset($data['map_default_center_latitude']) && is_numeric(trim($data['map_default_center_latitude']))) {
437 1
                $admin->saveSetting($category, 'map_default_center_latitude', (float) trim($data['map_default_center_latitude']));
438
            }
439 1
            if (!$this->is_valid_lng($data['map_default_center_longitude'])) $data['map_default_center_longitude'] = -99.5;
440 1
            if (isset($data['map_default_center_longitude']) && is_numeric(trim($data['map_default_center_longitude']))) {
441 1
                $admin->saveSetting($category, 'map_default_center_longitude', (float) trim($data['map_default_center_longitude']));
442
            }
443
444
            // Set Geocoding API URL or Proxy URL
445 1
            if (substr($data['geocoding_api_url'], 0, 4) != 'http' && substr($data['geocoding_api_url'], 0, 2) != '//') {
446 1
                $data['geocoding_api_url'] = $this->settings['geocoding_api_url'];
447
            }
448 1
            if (isset($data['geocoding_api_url'])) {
449 1
                $admin->saveSetting($category, 'geocoding_api_url', trim($data['geocoding_api_url']));
450
            }
451
            // Set Google Maps API Secret
452 1
            if (empty($data['geocoding_api_secret'])) $data['geocoding_api_secret'] = '';
453 1
            if (isset($data['geocoding_api_secret'])) {
454 1
                $admin->saveSetting($category, 'geocoding_api_secret', trim($data['geocoding_api_secret']));
455
            }
456
457 1
            return true;
458
        }
459
460 1
        return false;
461
    }
462
463
    /**
464
     * Update Geocode Information for $bean
465
     *
466
     * This is method is executed as a before_save logic hook.
467
     * With before_save, only the values are updated.
468
     * See before_save logic hooks
469
     *
470
     * defineMapsAddress() should find the address by the parent relationship if needed.
471
     * That is, the parent relationship should define the new maps address.
472
     *
473
     * $bean passed by reference
474
     *
475
     */
476 1
    function updateGeocodeInfo(&$bean, $after_save = false) {
477
478 1
        $GLOBALS['log']->info(__METHOD__.' START');
479 1
        if (empty($bean->id) || empty($bean->object_name) || empty($bean->module_name)) {
480 1
            return false;
481
        }
482 1
        $GLOBALS['log']->info(__METHOD__.' $bean->object_name: '.$bean->object_name);
483
484
        // Make sure we have the custom fields in our $bean
485 1
        if ($after_save === true) {
486
            $bean->custom_fields->retrieve();
487
        }
488 1
        $this->logGeocodeInfo($bean);
489
490
        // Define the Address Info based on the new bean data
491
        // $bean_data is not fetched_row data, but rather the properties of the bean as an array
492 1
        $bean_data = get_object_vars($bean);
493 1
        $aInfo = $this->defineMapsAddress($bean->object_name, $bean_data);
494 1
        $GLOBALS['log']->debug(__METHOD__.' $aInfo: '.print_r($aInfo, true));
495
496
        // If the related Account address has changed then reset the geocode values
497 1
        if (isset($aInfo['address']) && $aInfo['address'] != $bean->fetched_row['jjwg_maps_address_c']) {
498
499
            // If needed, check the Address Cache Module for Geocode Info
500
            if (!is_object($this->jjwg_Address_Cache)) {
501
                $this->jjwg_Address_Cache = get_module_info('jjwg_Address_Cache');
502
            }
503
            // Check Cache, if address is set
504
            if (!empty($aInfo['address']) && is_object($this->jjwg_Address_Cache)) {
505
506
                $aInfoCache = $this->jjwg_Address_Cache->getAddressCacheInfo($aInfo);
507
                $GLOBALS['log']->debug(__METHOD__.' $aInfoCache: '.print_r($aInfoCache, true));
508
                if (!empty($aInfoCache['address'])) {
509
                    $aInfo = $aInfoCache;
510
                }
511
            }
512
            // Update/Reset the Geocode fields
513
            $bean->jjwg_maps_lat_c = (!empty($aInfo['lat'])) ? $aInfo['lat'] : 0;
514
            $bean->jjwg_maps_lng_c = (!empty($aInfo['lng'])) ? $aInfo['lng'] : 0;
515
            $bean->jjwg_maps_geocode_status_c = (!empty($aInfo['status'])) ? $aInfo['status'] : '';
516
            $bean->jjwg_maps_address_c = (!empty($aInfo['address'])) ? $aInfo['address'] : '';
517
518
        }
519
520 1
    }
521
522
    /**
523
     * Update Geocode Information for $bean's Related Meetings
524
     *
525
     * This is done typically after the $bean is already saved.
526
     * See after_save logic hooks
527
     * Meetings related to: accounts, cases, contacts, leads
528
     *      opportunities, projects and prospects (targets)
529
     *
530
     * This method updates the meeting's address info based on
531
     * the current related bean address info.
532
     *
533
     * Note, it was decided to not define the Meeting beans to
534
     * improve performance and not trigger additional logic hooks.
535
     *
536
     * @param $bean
537
     */
538 1
    function updateRelatedMeetingsGeocodeInfo(&$bean) {
539
540 1
        $GLOBALS['log']->info(__METHOD__.' START');
541 1
        if (empty($bean->id) || empty($bean->object_name) || empty($bean->module_name)) {
542 1
            return false;
543
        }
544 1
        $GLOBALS['log']->info(__METHOD__.' $bean->object_name: '.$bean->object_name);
545
546
        // Check to see if address info is already set, or redefine
547 1
        $bean_data = get_object_vars($bean);
548 1
        $aInfo = $this->defineMapsAddress($bean->object_name, $bean_data);
549 1
        $GLOBALS['log']->debug(__METHOD__.' $bean_data: '.$bean_data);
550 1
        $GLOBALS['log']->debug(__METHOD__.' $aInfo: '.$aInfo);
551
552
        // If needed, check the Address Cache Module for Geocode Info
553 1
        if (!is_object($this->jjwg_Address_Cache)) {
554 1
            $this->jjwg_Address_Cache = get_module_info('jjwg_Address_Cache');
555
        }
556
        // Check Cache, if address is set
557 1
        if (!empty($aInfo['address']) && is_object($this->jjwg_Address_Cache)) {
558
559
            $aInfoCache = $this->jjwg_Address_Cache->getAddressCacheInfo($aInfo);
560
            $GLOBALS['log']->debug(__METHOD__.' $aInfoCache: '.$aInfoCache);
561
            if (!empty($aInfoCache['address'])) {
562
                $aInfo = $aInfoCache;
563
            }
564
        }
565
566 1
        $lngQ = (!empty($aInfo['lng'])) ? $this->db->quote($aInfo['lng']) : 0;
567 1
        $latQ = (!empty($aInfo['lat'])) ? $this->db->quote($aInfo['lat']) : 0;
568 1
        $statusQ = (!empty($aInfo['status'])) ? $this->db->quote($aInfo['status']) : '';
569 1
        $addressQ = (!empty($aInfo['address'])) ? $this->db->quote($aInfo['address']) : '';
570
571
        // Find the Related Meetings by parent_type & parent_id (this relationship is special)
572
        $query = "SELECT meetings.id, meetings_cstm.id_c FROM meetings " .
573
                " LEFT JOIN meetings_cstm ON meetings.id = meetings_cstm.id_c " .
574
                " WHERE meetings.deleted = 0 AND " .
575 1
                " meetings.parent_type = '" . $this->db->quote($bean->module_name) . "' AND " .
576 1
                " meetings.parent_id = '" . $this->db->quote($bean->id) . "'";
577 1
        $result = $this->db->query($query);
578
579 1
        while ($row = $this->db->fetchByAssoc($result)) {
580
581
            $idQ = $this->db->quote($row['id']);
582
            if (!empty($row['id_c'])) {
583
                // Update Custom Fields
584
                $query = "UPDATE meetings_cstm SET" .
585
                        " jjwg_maps_lat_c = '" . $latQ . "'," .
586
                        " jjwg_maps_lng_c = '" . $lngQ . "'," .
587
                        " jjwg_maps_geocode_status_c = '".$statusQ."'," .
588
                        " jjwg_maps_address_c = '" . $addressQ . "'" .
589
                        " WHERE id_c = '" . $idQ . "'";
590
                $update_result = $this->db->query($query);
591
            } else {
592
                // Insert Custom Fields
593
                $query = "INSERT INTO meetings_cstm" .
594
                        " (id_c, jjwg_maps_lat_c, jjwg_maps_lng_c, jjwg_maps_geocode_status_c, jjwg_maps_address_c)" .
595
                        " VALUES ('" . $idQ . "', '" . $latQ . "', '" . $lngQ . "', '".$statusQ."', '" . $addressQ . "') ";
596
                $insert_result = $this->db->query($query);
597
            }
598
599
        }
600 1
    }
601
602
    /**
603
     * Update Geocode Information for Meeting $bean
604
     *
605
     * Meeting Only. This method is executed as an after_save logic hook
606
     * due to the timing that parent_type and parent_id are saved.
607
     * The values are updated by queries in the updateGeocodeInfoByBeanQuery method.
608
     *
609
     * defineMapsAddress() should find the address by the parent relationship if needed.
610
     * That is, the parent relationship should define the new maps address.
611
     *
612
     * $bean passed by reference
613
     */
614 1
    function updateMeetingGeocodeInfo(&$bean) {
615
616 1
        $GLOBALS['log']->info(__METHOD__.' START');
617 1
        if (empty($bean->object_name)) {
618
            return false;
619
        }
620 1
        $GLOBALS['log']->info(__METHOD__.' $bean->object_name: '.$bean->object_name);
621
622
        // Make sure we have the custom fields in our $bean
623 1
        $bean->custom_fields->retrieve();
624 1
        $this->logGeocodeInfo($bean);
625
626
        // Define the Address Info based on the new bean data
627
        // $bean_data is not fetched_row data, but rather the properties of the bean as an array
628 1
        $bean_data = get_object_vars($bean);
629 1
        $aInfo = $this->defineMapsAddress($bean->object_name, $bean_data);
630 1
        $GLOBALS['log']->debug(__METHOD__.' $aInfo: '.print_r($aInfo, true));
631
632
        // If the related Account address has changed then reset the geocode values
633 1
        if (isset($aInfo['address']) && $aInfo['address'] != $bean->fetched_row['jjwg_maps_address_c']) {
634
635
            // If needed, check the Address Cache Module for Geocode Info
636
            if (!is_object($this->jjwg_Address_Cache)) {
637
                $this->jjwg_Address_Cache = get_module_info('jjwg_Address_Cache');
638
            }
639
            // Check Cache, if address is set
640
            if (!empty($aInfo['address']) && is_object($this->jjwg_Address_Cache)) {
641
642
                $aInfoCache = $this->jjwg_Address_Cache->getAddressCacheInfo($aInfo);
643
                $GLOBALS['log']->debug(__METHOD__.' $aInfoCache: '.print_r($aInfoCache, true));
644
                if (!empty($aInfoCache['address'])) {
645
                    $aInfo = $aInfoCache;
646
                }
647
            }
648
            // Update/Reset the Geocode fields using Queries (not save() bean method)
649
            $update_result = $this->updateGeocodeInfoByBeanQuery($bean, $aInfo);
650
        }
651
652 1
    }
653
654
    /**
655
     * Used to Update or Remove Geocoding Information from Custom Table
656
     * Simple Query Approach
657
     *
658
     * @param type $table_name string
659
     * @param type $display array (fetched_row)
660
     * @param type $aInfo   array
661
     */
662 1
    function updateGeocodeInfoByAssocQuery($table_name, $display, $aInfo = array()) {
663
664 1
        $GLOBALS['log']->info(__METHOD__.' START');
665 1
        if (empty($display['id']) || empty($table_name)) {
666 1
            return false;
667
        }
668 1
        $GLOBALS['log']->info(__METHOD__.' $display[\'id\']: '.$display['id']);
669
670 1
        if (!(in_array($table_name, $this->settings['valid_geocode_tables']))) {
671
            return false;
672
        }
673 1
        $idQ = $this->db->quote($display['id']);
674 1
        $lngQ = (!empty($aInfo['lng'])) ? $this->db->quote($aInfo['lng']) : 0;
675 1
        $latQ = (!empty($aInfo['lat'])) ? $this->db->quote($aInfo['lat']) : 0;
676 1
        $statusQ = (!empty($aInfo['status'])) ? $this->db->quote($aInfo['status']) : '';
677 1
        $addressQ = (!empty($aInfo['address'])) ? $this->db->quote($aInfo['address']) : '';
678
679
        // Find record by id
680 1
        $query = "SELECT ".$table_name.".id, ".$table_name."_cstm.id_c FROM ".$table_name." " .
681 1
                " LEFT JOIN ".$table_name."_cstm ON ".$table_name.".id = ".$table_name."_cstm.id_c " .
682 1
                " WHERE ".$table_name.".deleted = 0 AND ".$table_name.".id = '" . $idQ . "'";
683 1
        $result = $this->db->query($query);
684 1
        $row = $this->db->fetchByAssoc($result);
685
686 1
        if (!empty($row['id_c'])) {
687
            // Update Custom Fields
688
            $query = "UPDATE " . $table_name . "_cstm SET" .
689
                    " jjwg_maps_lat_c = '" . $latQ . "'," .
690
                    " jjwg_maps_lng_c = '" . $lngQ . "'," .
691
                    " jjwg_maps_geocode_status_c = '".$statusQ."'," .
692
                    " jjwg_maps_address_c = '" . $addressQ . "'" .
693
                    " WHERE id_c = '" . $idQ . "'";
694
            $update_result = $this->db->query($query);
695
        } else {
696
            // Insert Custom Fields
697 1
            $query = "INSERT INTO " . $table_name . "_cstm" .
698 1
                    " (id_c, jjwg_maps_lat_c, jjwg_maps_lng_c, jjwg_maps_geocode_status_c, jjwg_maps_address_c)" .
699 1
                    " VALUES ('" . $idQ . "', '" . $latQ . "', '" . $lngQ . "', '".$statusQ."', '" . $addressQ . "') ";
700 1
            $insert_result = $this->db->query($query);
701
        }
702
703 1
    }
704
705
    /**
706
     * Used to Update or Remove Geocoding Information from Custom Table
707
     * Simple Query Approach
708
     *
709
     * @param type $bean    object
710
     * @param type $aInfo   array
711
     */
712 1
    function updateGeocodeInfoByBeanQuery(&$bean, $aInfo = array()) {
713
714 1
        $GLOBALS['log']->info(__METHOD__.' START');
715 1
        if (empty($bean->id) || empty($bean->object_name) || empty($bean->table_name)) {
716 1
            return false;
717
        }
718 1
        $GLOBALS['log']->info(__METHOD__.' $bean->object_name: '.$bean->object_name);
719
720 1
        $table_name = $bean->table_name;
721 1
        if (!(in_array($table_name, $this->settings['valid_geocode_tables']))) {
722
            return false;
723
        }
724 1
        $idQ = $this->db->quote($bean->id);
725 1
        $lngQ = (!empty($aInfo['lng'])) ? $this->db->quote($aInfo['lng']) : 0;
726 1
        $latQ = (!empty($aInfo['lat'])) ? $this->db->quote($aInfo['lat']) : 0;
727 1
        $statusQ = (!empty($aInfo['status'])) ? $this->db->quote($aInfo['status']) : '';
728 1
        $addressQ = (!empty($aInfo['address'])) ? $this->db->quote($aInfo['address']) : '';
729
730
        // Find record by id
731 1
        $query = "SELECT ".$table_name.".id, ".$table_name."_cstm.id_c FROM ".$table_name." " .
732 1
                " LEFT JOIN ".$table_name."_cstm ON ".$table_name.".id = ".$table_name."_cstm.id_c " .
733 1
                " WHERE ".$table_name.".deleted = 0 AND ".$table_name.".id = '" . $idQ . "'";
734 1
        $result = $this->db->query($query);
735 1
        $row = $this->db->fetchByAssoc($result);
736
737 1
        if (!empty($row['id_c'])) {
738
            // Update Custom Fields
739
            $query = "UPDATE " . $table_name . "_cstm SET" .
740
                    " jjwg_maps_lat_c = '" . $latQ . "'," .
741
                    " jjwg_maps_lng_c = '" . $lngQ . "'," .
742
                    " jjwg_maps_geocode_status_c = '".$statusQ."'," .
743
                    " jjwg_maps_address_c = '" . $addressQ . "'" .
744
                    " WHERE id_c = '" . $idQ . "'";
745
            $update_result = $this->db->query($query);
746
        } else {
747
            // Insert Custom Fields
748 1
            $query = "INSERT INTO " . $table_name . "_cstm" .
749 1
                    " (id_c, jjwg_maps_lat_c, jjwg_maps_lng_c, jjwg_maps_geocode_status_c, jjwg_maps_address_c)" .
750 1
                    " VALUES ('" . $idQ . "', '" . $latQ . "', '" . $lngQ . "', '".$statusQ."', '" . $addressQ . "') ";
751 1
            $insert_result = $this->db->query($query);
752
        }
753
754 1
    }
755
756
    /**
757
     * Remove All Geocoding Information from Custom Table for Module
758
     * Simple Query Approach
759
     *
760
     * @param type $bean    object
761
     */
762 1
    function deleteAllGeocodeInfoByBeanQuery(&$bean) {
763
764 1
        $GLOBALS['log']->info(__METHOD__.' START');
765 1
        if (empty($bean->object_name) || empty($bean->table_name)) {
766
            return false;
767
        }
768 1
        $GLOBALS['log']->info(__METHOD__.' $bean->object_name: '.$bean->object_name);
769
770 1
        $table_name = $bean->table_name;
771 1
        if (!(in_array($table_name, $this->settings['valid_geocode_tables']))) {
772 1
            return false;
773
        }
774
775
        // Update Fields to NULL
776 1
        $query = "UPDATE " . $table_name . "_cstm SET" .
777 1
                " jjwg_maps_lat_c = NULL," .
778 1
                " jjwg_maps_lng_c = NULL," .
779 1
                " jjwg_maps_geocode_status_c = NULL," .
780 1
                " jjwg_maps_address_c = NULL" .
781 1
                " WHERE 1=1";
782 1
        $update_result = $this->db->query($query);
783 1
    }
784
785
    /**
786
     * Get $db result of records (addresses) in need of geocoding
787
     * @param $table_name string
788
     * @param $limit integer
789
     * @param $id string
790
     */
791 1
    function getGeocodeAddressesResult($table_name, $limit = 0, $id = '') {
792
793 1
        if (!(in_array($table_name, $this->settings['valid_geocode_tables']))) {
794 1
            return false;
795
        }
796 1
        if (empty($limit) || !is_int($limit)) {
797 1
            $limit = $this->settings['geocoding_limit'];
798
        }
799
        // Find the Items to Geocode
800
        // Assume there is no address at 0,0; it's in the Atlantic Ocean
801
        $where_conds = "(" .
802 1
                "(" . $table_name . "_cstm.jjwg_maps_lat_c = 0 AND " .
803 1
                "" . $table_name . "_cstm.jjwg_maps_lng_c = 0)" .
804 1
                " OR " .
805 1
                "(" . $table_name . "_cstm.jjwg_maps_lat_c IS NULL AND " .
806 1
                "" . $table_name . "_cstm.jjwg_maps_lng_c IS NULL)" .
807 1
                ")" .
808 1
                " AND " .
809 1
                "(" . $table_name . "_cstm.jjwg_maps_geocode_status_c = '' OR " .
810 1
                "" . $table_name . "_cstm.jjwg_maps_geocode_status_c IS NULL)";
811
        // Limit to only one result
812 1
        if (@is_guid($id)) {
813
            $where_conds .= " AND id = '".$id."'";
814
        }
815
        // Create Simple Query
816
        // Note: Do not use create_new_list_query() or process_list_query()
817
        // as they will typically exceeded memory allowed.
818 1
        $query = "SELECT " . $table_name . ".*, " . $table_name . "_cstm.* FROM " . $table_name .
819 1
                " LEFT JOIN " . $table_name . "_cstm " .
820 1
                " ON " . $table_name . ".id = " . $table_name . "_cstm.id_c " .
821 1
                " WHERE " . $table_name . ".deleted = 0 AND " . $where_conds;
822 1
        $display_result = $this->db->limitQuery($query, 0, $limit);
823
824 1
        return $display_result;
825
    }
826
827
    /**
828
     * getGoogleMapsGeocode - Get Lng/Lat using Google Maps V3
829
     * @var $address
830
     * @var $return_full_array boolean
831
     * @var $allow_approximate boolean
832
     */
833 1
    function getGoogleMapsGeocode($address, $return_full_array = false, $allow_approximate = true) {
834
835 1
        $GLOBALS['log']->debug(__METHOD__.' START');
836 1
        $GLOBALS['log']->info(__METHOD__.' $address: '.$address);
837
838
        /* allow_approximate_location_type - overrides only to true */
839 1
        if (!empty($this->settings['allow_approximate_location_type'])) {
840
            $allow_approximate = true;
841
        }
842
843
        /**
844
         * Google Maps API v3 - The new v3 Google Maps API no longer requires a Maps API Key!
845
         * Old Default: https://maps.google.com/maps/api/geocode/json?sensor=false
846
         * New Default: https://maps.googleapis.com/maps/api/geocode/json?sensor=false
847
         */
848 1
        $base_url = $this->settings['geocoding_api_url'];
849 1
        if (!(strpos($base_url, '?') > 0)) $base_url .= '?';
850
        // Add Address Parameter
851 1
        $request_url = $base_url . "&address=" . urlencode($address);
852
        // Add Hash Parameter as MD5 of Concatenation of Address and Secret
853 1
        if (!empty($this->settings['geocoding_api_secret'])) {
854
            $hash = md5($address.$this->settings['geocoding_api_secret']);
855
            $request_url .= '&hash='.urlencode($hash);
856
        }
857
858 1
        $GLOBALS['log']->info(__METHOD__.' cURL Request URL: '.$request_url);
859
860 1
        $ch = curl_init();
861 1
        curl_setopt($ch, CURLOPT_URL, $request_url);
862 1
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1");
863 1
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
864 1
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
865 1
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
866 1
        $json_contents = curl_exec($ch);
867
868
        // Debug: Error Handling
869 1
        $ch_errno = curl_errno($ch);
870 1
        $ch_error = curl_error($ch);
871 1
        if (!empty($ch_errno) || !empty($ch_error)) {
872
            $GLOBALS['log']->error(__METHOD__.' cURL Error: #'.$ch_errno.' - '.$ch_error);
873
        }
874
875 1
        curl_close($ch);
876 1
        $GLOBALS['log']->debug(__METHOD__.' $json_contents: '.$json_contents);
877 1
        $googlemaps = json_decode($json_contents, true);
878 1
        $GLOBALS['log']->debug(__METHOD__.' $googlemaps: '.$googlemaps);
879
880
        /**
881
         * https://developers.google.com/maps/documentation/geocoding/#Results
882
         * Status: "OK" : geocoding was successful
883
         * "ZERO_RESULTS" : indicates that the geocode was successful but returned no results
884
         * "OVER_QUERY_LIMIT" : indicates that you are over your quota
885
         * "REQUEST_DENIED" : lack of sensor parameter
886
         * "INVALID_REQUEST" generally indicates that the query (address or lat/lng) is missing.
887
         * Limit to location_type = 'ROOFTOP', 'RANGE_INTERPOLATED' or 'GEOMETRIC_CENTER' but not 'APPROXIMATE'
888
         */
889 1
        $aInfo = array('address' => $address);
890 1
        if (!empty($googlemaps) && isset($googlemaps['status'])) {
891 1
            if ($googlemaps['status'] == 'OVER_QUERY_LIMIT') {
892
            // Debug: Log Over Limit
893
                $GLOBALS['log']->warn(__METHOD__.' Google Maps API Status of OVER_QUERY_LIMIT: Over Your Quota');
894 1
            } elseif (!$allow_approximate && $googlemaps['results'][0]['geometry']['location_type'] == 'APPROXIMATE') {
895
                // Consider 'APPROXIMATE' to be similar to 'ZERO_RESULTS'
896
                @$aInfo = array(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
897
                    'address' => $address,
898
                    'status' => 'APPROXIMATE',
899
                    'lat' => $googlemaps['results'][0]['geometry']['location']['lat'],
900
                    'lng' => $googlemaps['results'][0]['geometry']['location']['lng']
901
                );
902
            } else {
903
                // Return address info
904 1
                @$aInfo = array(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
905 1
                    'address' => $address,
906 1
                    'status' => $googlemaps['status'],
907 1
                    'lat' => $googlemaps['results'][0]['geometry']['location']['lat'],
908 1
                    'lng' => $googlemaps['results'][0]['geometry']['location']['lng']
909
                );
910
            }
911
        }
912
913 1
        if ($return_full_array) {
914
            $GLOBALS['log']->debug(__METHOD__.' $googlemaps: '.print_r($googlemaps, true));
915
            return $googlemaps;
916
        } else {
917 1
            $GLOBALS['log']->debug(__METHOD__.' $aInfo: '.print_r($aInfo, true));
918 1
            return $aInfo;
919
        }
920
    }
921
922
    /**
923
     * Define Maps Address
924
     *
925
     * Address Relationship Notes:
926
     * Account(address)
927
     * Contact(address)
928
     * Lead(address)
929
     * Taget/Prospect(address)
930
     * Opportunity to Account(address)
931
     * Case 'account_id' to Account(address)
932
     *   or Case to Account(address)
933
     * Project to Account(address)
934
     *   or Project to Opportunity to Account(address)
935
     * Meeting - based on Flex Relate
936
     *
937
     * @param $object_name  signular object name
938
     * @param $display      fetched row
939
     */
940 4
    function defineMapsAddress($object_name, $display) {
941
942 4
        $address = false;
943 4
        $fields = false;
944 4
        $parent = null;
945 4
        $parent_type = '';
946 4
        $parent_id = '';
947
948 4
        $GLOBALS['log']->debug(__METHOD__.' START');
949 4
        $GLOBALS['log']->debug(__METHOD__.' $object_name: '.$object_name);
950 4
        $GLOBALS['log']->debug(__METHOD__.' $display: '.print_r($display, true));
951
952
        // Field naming is different in some modules.
953
        // Some modules do not have an address, so a related account needs to be found first.
954
955 4
        if ($object_name == 'Account') {
956
957 2
            $address = $this->defineMapsFormattedAddress($display, $this->settings['geocode_modules_to_address_type']['Accounts']);
958
959 3
        } elseif ($object_name == 'Contact') {
960
961 1
            $address = $this->defineMapsFormattedAddress($display, $this->settings['geocode_modules_to_address_type']['Contacts']);
962
963 3
        } elseif ($object_name == 'Lead') {
964
965 1
            $address = $this->defineMapsFormattedAddress($display, $this->settings['geocode_modules_to_address_type']['Leads']);
966
967 3
        } elseif ($object_name == 'Prospect') {
968
969
            $address = $this->defineMapsFormattedAddress($display, $this->settings['geocode_modules_to_address_type']['Prospects']);
970
971 3
        } elseif ($object_name == 'User') {
972
973
            $address = $this->defineMapsFormattedAddress($display, $this->settings['geocode_modules_to_address_type']['Users']);
974
975 3
        } elseif ($object_name == 'Opportunity') {
976
977
            // Find Account - Assume only one related Account
978
            $query = "SELECT accounts.*, accounts_cstm.* FROM accounts LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c " .
979
                    " LEFT JOIN accounts_opportunities ON accounts.id = accounts_opportunities.account_id AND accounts_opportunities.deleted = 0 " .
980 1
                    " WHERE accounts.deleted = 0 AND accounts_opportunities.opportunity_id = '" . $display['id'] . "'";
981 1
            $GLOBALS['log']->debug(__METHOD__.' Opportunity to Account');
982 1
            $result = $this->db->limitQuery($query, 0, 1);
983 1
            $fields = $this->db->fetchByAssoc($result);
984
985 1
            if (!empty($fields)) {
986 1
                $address = $this->defineMapsFormattedAddress($fields, $this->settings['geocode_modules_to_address_type']['Opportunities']);
987
            }
988
989 3
        } elseif (in_array($object_name, array('aCase', 'Case'))) {
990
991
            // Find Account from Case (account_id field)
992
            $query = "SELECT accounts.*, accounts_cstm.* FROM accounts LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c " .
993 1
                    " WHERE accounts.deleted = 0 AND id = '" . $display['account_id'] . "'";
994 1
            $GLOBALS['log']->debug(__METHOD__.' Case to Account');
995 1
            $result = $this->db->limitQuery($query, 0, 1);
996 1
            $fields = $this->db->fetchByAssoc($result);
997
998
            // If Account is not found; Find many to many Account - Assume only one related Account
999 1
            if (empty($fields)) {
1000
                $query = "SELECT accounts.*, accounts_cstm.* FROM accounts LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c " .
1001
                        " LEFT JOIN accounts_cases ON accounts.id = accounts_cases.account_id AND accounts_cases.deleted = 0 " .
1002 1
                        " WHERE accounts.deleted = 0 AND accounts_cases.case_id = '" . $display['id'] . "'";
1003 1
                $GLOBALS['log']->debug(__METHOD__.' Case to Accounts');
1004 1
                $result = $this->db->limitQuery($query, 0, 1);
1005 1
                $fields = $this->db->fetchByAssoc($result);
1006
            }
1007
1008 1
            if (!empty($fields)) {
1009 1
                $address = $this->defineMapsFormattedAddress($fields, $this->settings['geocode_modules_to_address_type']['Cases']);
1010
            }
1011
1012 3
        } elseif ($object_name == 'Project') {
1013
1014
            // Check relationship from Project to Account - Assume only one related Account
1015
            $query = "SELECT accounts.*, accounts_cstm.* FROM accounts LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c " .
1016
                    " LEFT JOIN projects_accounts ON accounts.id = projects_accounts.account_id AND projects_accounts.deleted = 0 " .
1017 1
                    " WHERE accounts.deleted = 0 AND projects_accounts.project_id = '" . $display['id'] . "'";
1018 1
            $GLOBALS['log']->debug(__METHOD__.' Project to Account');
1019 1
            $result = $this->db->limitQuery($query, 0, 1);
1020 1
            $fields = $this->db->fetchByAssoc($result);
1021
1022 1
            if (empty($fields)) {
1023
                // Find Opportunity - Assuming that the Project was created from an Opportunity (Closed Won) Detial View
1024
                $query = "SELECT opportunities.*, opportunities_cstm.* FROM opportunities LEFT JOIN opportunities_cstm ON opportunities.id = opportunities_cstm.id_c " .
1025
                        " LEFT JOIN projects_opportunities ON opportunities.id = projects_opportunities.opportunity_id AND projects_opportunities.deleted = 0 " .
1026 1
                        " WHERE opportunities.deleted = 0 AND projects_opportunities.project_id = '" . $display['id'] . "'";
1027 1
                $GLOBALS['log']->debug(__METHOD__.' Project to Opportunity');
1028 1
                $result = $this->db->limitQuery($query, 0, 1);
1029 1
                $opportunity = $this->db->fetchByAssoc($result);
1030
                // Find Account - Assume only one related Account for the Opportunity
1031
                $query = "SELECT accounts.*, accounts_cstm.* FROM accounts LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c " .
1032
                        " LEFT JOIN accounts_opportunities ON accounts.id = accounts_opportunities.account_id AND accounts_opportunities.deleted = 0 " .
1033 1
                        " WHERE accounts.deleted = 0 AND accounts_opportunities.opportunity_id = '" . $opportunity['id'] . "'";
1034 1
                $GLOBALS['log']->debug(__METHOD__.' Opportunity to Account');
1035 1
                $result = $this->db->limitQuery($query, 0, 1);
1036 1
                $fields = $this->db->fetchByAssoc($result);
1037
            }
1038
1039 1
            if (!empty($fields)) {
1040 1
                $address = $this->defineMapsFormattedAddress($fields, $this->settings['geocode_modules_to_address_type']['Project']);
1041
            }
1042
1043 3
        } elseif ($object_name == 'Meeting') {
1044
1045
            // Find Meeting - Flex Relate Fields: meetings.parent_type and meetings.parent_id
1046
            $query = "SELECT meetings.*, meetings_cstm.* FROM meetings LEFT JOIN meetings_cstm ON meetings.id = meetings_cstm.id_c " .
1047 3
                    " WHERE meetings.deleted = 0 AND meetings.id = '" . $display['id'] . "'";
1048 3
            $GLOBALS['log']->debug(__METHOD__.' Meeting');
1049 3
            $result = $this->db->limitQuery($query, 0, 1);
1050 3
            $meeting = $this->db->fetchByAssoc($result);
1051
1052 3
            $parent_type = $meeting['parent_type'];
1053 3
            $parent_id = $meeting['parent_id'];
1054 3
            $GLOBALS['log']->debug(__METHOD__.' Meeting $parent_type: '.$parent_type);
1055 3
            $GLOBALS['log']->debug(__METHOD__.' Meeting $parent_id: '.$parent_id);
1056
1057
            // If the parent_type is valid module to geocode
1058 3
            if (in_array($parent_type, array_keys($this->settings['valid_geocode_modules']))
1059 3
                    && !empty($parent_id) && $parent_type != 'Meeting') {
1060
1061
                // Define parent object
1062
                $parent = get_module_info($parent_type);
1063
                $parent->retrieve($parent_id);
1064
                $parent->custom_fields->retrieve();
1065
                $fields = $parent->fetched_row;
1066
1067
                $GLOBALS['log']->debug(__METHOD__.' Meeting $parent->object_name: '.$parent->object_name);
1068
                $GLOBALS['log']->debug(__METHOD__.' Meeting $parent->fetched_row: '.print_r($parent->fetched_row, true));
1069
1070
                // Call this defineMapsAddress for parent which will look at other relationships
1071
                $aInfo = $this->defineMapsAddress($parent->object_name, $parent->fetched_row);
1072
                // return $aInfo
1073
                $GLOBALS['log']->debug(__METHOD__.' Meeting $address Found $aInfo: '.print_r($aInfo, true));
1074
                return $aInfo;
1075
            }
1076
1077
        }
1078
1079
1080
        // If related account address has already been geocoded
1081 4
        if (!empty($address) && $fields['jjwg_maps_geocode_status_c'] == 'OK' &&
1082 4
                !empty($fields['jjwg_maps_lat_c']) && !empty($fields['jjwg_maps_lng_c'])) {
1083
            $aInfo = array(
1084
                'address' => $address,
1085
                'status' => 'OK',
1086
                'lat' => $fields['jjwg_maps_lat_c'],
1087
                'lng' => $fields['jjwg_maps_lng_c']
1088
            );
1089
            $GLOBALS['log']->debug(__METHOD__.' OK Array Found $aInfo: '.print_r($aInfo, true));
1090
            return $aInfo;
1091
        // elseif return address only - if defined
1092 4
        } elseif (!empty($address)) {
1093
            $aInfo = array(
1094 1
                'address' => $address,
1095
            );
1096 1
            $GLOBALS['log']->debug(__METHOD__.' $address Found $aInfo: '.print_r($aInfo, true));
1097 1
            return $aInfo;
1098
        } else {
1099 4
            return false;
1100
        }
1101
1102
    }
1103
1104
    /**
1105
     * Define the formatted address line based on address type and field names
1106
     * @param $display bean fields array
1107
     * @param $type type of address: 'billing', 'shipping', 'primary', 'alt', 'custom', 'address'
1108
     */
1109 3
    function defineMapsFormattedAddress($display, $type = 'billing') {
1110
1111 3
        $type = strtolower($type);
1112 3
        if (!in_array($type, array('billing', 'shipping', 'primary', 'alt', 'custom', 'address')))
1113
            $type = 'billing';
1114 3
        $GLOBALS['log']->debug(__METHOD__.' $type: '.print_r($type, true));
1115 3
        $address_fields = array('billing_address_street', 'billing_address_city', 'billing_address_state', 'billing_address_postalcode', 'billing_address_country');
1116 3
        $address_parts = array();
1117
        switch ($type) {
1118 3
            case 'billing':
1119 3
                $address_fields = array('billing_address_street', 'billing_address_city', 'billing_address_state', 'billing_address_postalcode', 'billing_address_country');
1120 3
                break;
1121 2
            case 'shipping':
1122 1
                $address_fields = array('shipping_address_street', 'shipping_address_city', 'shipping_address_state', 'shipping_address_postalcode', 'shipping_address_country');
1123 1
                break;
1124 2
            case 'primary':
1125 2
                $address_fields = array('primary_address_street', 'primary_address_city', 'primary_address_state', 'primary_address_postalcode', 'primary_address_country');
1126 2
                break;
1127 1
            case 'alt':
1128 1
                $address_fields = array('alt_address_street', 'alt_address_city', 'alt_address_state', 'alt_address_postalcode', 'alt_address_country');
1129 1
                break;
1130 1
            case 'address':
1131 1
                $address_fields = array('address_street', 'address_city', 'address_state', 'address_postalcode', 'address_country');
1132 1
                break;
1133
        }
1134 3
        $GLOBALS['log']->debug(__METHOD__.' $address_fields: '.print_r($address_fields, true));
1135 3
        foreach ($address_fields as $field) {
1136 3
            if (!isset($display[$field]))
1137 2
                $display[$field] = '';
1138 3
            if (!empty($display[$field]))
1139 3
                $address_parts[] = trim($display[$field]);
1140
        }
1141 3
        if (strlen(implode('', $address_parts)) > 3) {
1142 2
            $address = implode(', ', $address_parts);
1143 2
            $address = preg_replace('/[\n\r]+/', ' ', trim($address));
1144 2
            $address = preg_replace("/[\t\s]+/", ' ', $address);
1145 2
            $GLOBALS['log']->debug(__METHOD__.' $address: '.print_r($address, true));
1146 2
            return trim($address);
1147
        } else {
1148 2
            return false;
1149
        }
1150
    }
1151
1152
    /**
1153
     * Check for valid longitude
1154
     * @param $lng float
1155
     */
1156 2
    function is_valid_lng($lng) {
1157 2
        return (is_numeric($lng) && $lng >= -180 && $lng <= 180);
1158
    }
1159
1160
    /**
1161
     * Check for valid latitude
1162
     * @param $lat float
1163
     */
1164 2
    function is_valid_lat($lat) {
1165 2
        return (is_numeric($lat) && $lat >= -90 && $lat <= 90);
1166
    }
1167
1168
    /**
1169
     * Bean Log Special
1170
     * This log method filters the $bean into a more readable array
1171
     */
1172 3
    function logGeocodeInfo($bean) {
1173
1174
        $log_keys = array(
1175 3
            'jjwg_maps_lat_c', 'jjwg_maps_lng_c', 'jjwg_maps_address_c', 'jjwg_maps_geocode_status_c',
1176
            'fetched_row', 'parent_id', 'parent_type', 'last_parent_id', 'rel_fields_before_value'
1177
        );
1178 3
        $log_output = array();
1179 3
        foreach (get_object_vars($bean) as $key=>$value) {
1180 3
            if (in_array($key, $log_keys)) {
1181 3
                $log_output[$key] = $value;
1182
            }
1183
        }
1184 3
        ksort($log_output);
1185 3
        $GLOBALS['log']->info(__METHOD__.' $log_output: '.print_r($log_output, true));
1186 3
    }
1187
}
1188
1189
1190
1191
/******************************************************************************/
1192
1193
1194
/**
1195
 * Function for Custom Dropdown of Target Lists
1196
 */
1197
1198 1
function getProspectLists()
1199
{
1200 1
    $query = "SELECT id, name, list_type FROM prospect_lists WHERE deleted = 0 ORDER BY name ASC";
1201 1
    $result = $GLOBALS['db']->query($query, false);
1202
1203 1
    $list = array();
1204 1
    $list['']='';
1205 1
    while (($row = $GLOBALS['db']->fetchByAssoc($result)) != null) {
1206
        $list[$row['id']] = $row['name'];
1207
    }
1208
1209
    return $list;
1210
}