3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
29 * A PHP cron script to format all the addresses in the database. Currently
30 * it only does geocoding if the geocode values are not set. At a later
31 * stage we will also handle USPS address cleanup and other formatting
35 class CRM_Utils_Address_BatchUpdate
{
43 var $returnMessages = array();
47 * @param array $params
49 public function __construct($params) {
51 foreach ($params as $name => $value) {
52 $this->$name = $value;
55 // fixme: more params verification
61 public function run() {
63 $config = &CRM_Core_Config
::singleton();
65 // do check for geocoding.
66 $processGeocode = FALSE;
67 if (empty($config->geocodeMethod
)) {
68 if (CRM_Utils_String
::strtobool($this->geocoding
) === TRUE) {
69 $this->returnMessages
[] = ts('Error: You need to set a mapping provider under Administer > System Settings > Mapping and Geocoding');
70 $this->returnError
= 1;
71 $this->returnResult();
75 $processGeocode = TRUE;
76 // user might want to over-ride.
77 if (CRM_Utils_String
::strtobool($this->geocoding
) === FALSE) {
78 $processGeocode = FALSE;
82 // do check for parse street address.
83 $parseAddress = FALSE;
84 $parseAddress = CRM_Utils_Array
::value('street_address_parsing',
85 CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
90 $parseStreetAddress = FALSE;
92 if (CRM_Utils_String
::strtobool($this->parse
) === TRUE) {
93 $this->returnMessages
[] = ts('Error: You need to enable Street Address Parsing under Administer > Localization > Address Settings.');
94 $this->returnError
= 1;
95 return $this->returnResult();
99 $parseStreetAddress = TRUE;
100 // user might want to over-ride.
101 if (CRM_Utils_String
::strtobool($this->parse
) === FALSE) {
102 $parseStreetAddress = FALSE;
107 if (!$parseStreetAddress && !$processGeocode) {
108 $this->returnMessages
[] = ts('Error: Both Geocode mapping as well as Street Address Parsing are disabled. You must configure one or both options to use this script.');
109 $this->returnError
= 1;
110 return $this->returnResult();
113 // do check for parse street address.
114 return $this->processContacts($config, $processGeocode, $parseStreetAddress);
119 * @param $processGeocode
120 * @param $parseStreetAddress
125 function processContacts(&$config, $processGeocode, $parseStreetAddress) {
126 // build where clause.
127 $clause = array('( c.id = a.contact_id )');
130 $clause[] = "( c.id >= %1 )";
131 $params[1] = array($this->start
, 'Integer');
135 $clause[] = "( c.id <= %2 )";
136 $params[2] = array($this->end
, 'Integer');
139 if ($processGeocode) {
140 $clause[] = '( a.geo_code_1 is null OR a.geo_code_1 = 0 )';
141 $clause[] = '( a.geo_code_2 is null OR a.geo_code_2 = 0 )';
142 $clause[] = '( a.country_id is not null )';
145 $whereClause = implode(' AND ', $clause);
155 FROM civicrm_contact c
156 INNER JOIN civicrm_address a ON a.contact_id = c.id
157 LEFT JOIN civicrm_country o ON a.country_id = o.id
158 LEFT JOIN civicrm_state_province s ON a.state_province_id = s.id
163 $totalGeocoded = $totalAddresses = $totalAddressParsed = 0;
165 $dao = CRM_Core_DAO
::executeQuery($query, $params);
166 if ($processGeocode) {
167 require_once (str_replace('_', DIRECTORY_SEPARATOR
, $config->geocodeMethod
) . '.php');
171 $unparseableContactAddress = array();
172 while ($dao->fetch()) {
175 'street_address' => $dao->street_address
,
176 'postal_code' => $dao->postal_code
,
177 'city' => $dao->city
,
178 'state_province' => $dao->state
,
179 'country' => $dao->country
,
182 $addressParams = array();
185 if ($processGeocode) {
186 // loop through the address removing more information
187 // so we can get some geocode for a partial address
188 // i.e. city -> state -> country
192 if ($this->throttle
) {
196 $className = $config->geocodeMethod
;
197 $className::format( $params, true );
199 // see if we got a geocode error, in this case we'll trigger a fatal
202 isset($params['geo_code_error']) &&
203 $params['geo_code_error'] == 'OVER_QUERY_LIMIT'
205 CRM_Core_Error
::fatal('Aborting batch geocoding. Hit the over query limit on geocoder.');
208 array_shift($params);
211 (!isset($params['geo_code_1']) ||
$params['geo_code_1'] == 'null') &&
215 if (isset($params['geo_code_1']) && $params['geo_code_1'] != 'null') {
217 $addressParams['geo_code_1'] = $params['geo_code_1'];
218 $addressParams['geo_code_2'] = $params['geo_code_2'];
219 $addressParams['postal_code'] = $params['postal_code'];
220 $addressParams['postal_code_suffix'] = $params['postal_code_suffix'];
224 // parse street address
225 if ($parseStreetAddress) {
226 $parsedFields = CRM_Core_BAO_Address
::parseStreetAddress($dao->street_address
);
228 // consider address is automatically parseable,
229 // when we should found street_number and street_name
230 if (empty($parsedFields['street_name']) ||
empty($parsedFields['street_number'])) {
234 // do check for all elements.
236 $totalAddressParsed++
;
238 elseif ($dao->street_address
) {
239 //build contact edit url,
240 //so that user can manually fill the street address fields if the street address is not parsed, CRM-5886
241 $url = CRM_Utils_System
::url('civicrm/contact/add', "reset=1&action=update&cid={$dao->id}");
242 $unparseableContactAddress[] = " Contact ID: " . $dao->id
. " <a href =\"$url\"> " . $dao->street_address
. " </a> ";
243 // reset element values.
244 $parsedFields = array_fill_keys(array_keys($parsedFields), '');
246 $addressParams = array_merge($addressParams, $parsedFields);
249 // finally update address object.
250 if (!empty($addressParams)) {
251 $address = new CRM_Core_DAO_Address();
252 $address->id
= $dao->address_id
;
253 $address->copyValues($addressParams);
259 $this->returnMessages
[] = ts("Addresses Evaluated: %1", array(
260 1 => $totalAddresses)) . "\n";
261 if ($processGeocode) {
262 $this->returnMessages
[] = ts("Addresses Geocoded: %1", array(
263 1 => $totalGeocoded)) . "\n";
265 if ($parseStreetAddress) {
266 $this->returnMessages
[] = ts("Street Addresses Parsed: %1", array(
267 1 => $totalAddressParsed)) . "\n";
268 if ($unparseableContactAddress) {
269 $this->returnMessages
[] = "<br />\n" . ts("Following is the list of contacts whose address is not parsed:") . "<br />\n";
270 foreach ($unparseableContactAddress as $contactLink) {
271 $this->returnMessages
[] = $contactLink . "<br />\n";
276 return $this->returnResult();
282 function returnResult() {
284 $result['is_error'] = $this->returnError
;
285 $result['messages'] = implode("", $this->returnMessages
);