diff --git a/class/UcAddressesAddress.class.php b/class/UcAddressesAddress.class.php index 3441a0e..a188a4d 100644 --- a/class/UcAddressesAddress.class.php +++ b/class/UcAddressesAddress.class.php @@ -488,6 +488,26 @@ class UcAddressesAddress extends UcAddressesSchemaAddress { return theme('uc_addresses_list_address', array('address' => $this)); } + /** + * Export address instance to PHP code. + * + * @return string + * PHP-code. + */ + public function varExport() { + $data = $this->getRawFieldData(); + $code = '$fields = ' . var_export($data, TRUE) . ";\n"; + $uid = $this->getUserId(); + if ($uid) { + $code .= '$address = UcAddressesAddressBook::get(' . $uid . ')->addAddress();' . "\n"; + } + else { + $code .= '$address = UcAddressesAddressBook::newAddress();' . "\n"; + } + $code .= '$address->setMultipleFields($fields);' . "\n"; + return $code; + } + // ----------------------------------------------------------------------------- // Low-level calls intended only for UcAddressesAddressBook // ----------------------------------------------------------------------------- diff --git a/class/UcAddressesAddressBook.class.php b/class/UcAddressesAddressBook.class.php index 279a6dc..112c666 100644 --- a/class/UcAddressesAddressBook.class.php +++ b/class/UcAddressesAddressBook.class.php @@ -151,6 +151,16 @@ class UcAddressesAddressBook { } /** + * Returns all currently loaded address books. + * + * @return array + * An array of UcAddressesAddressBook instances. + */ + static public function getAddressBooks() { + return self::$singleton; + } + + /** * Looks up a single address * * This method will first look in all the loaded address books if the address diff --git a/feeds/FeedsUcAddressesProcessor.inc b/feeds/FeedsUcAddressesProcessor.inc new file mode 100644 index 0000000..31db09f --- /dev/null +++ b/feeds/FeedsUcAddressesProcessor.inc @@ -0,0 +1,258 @@ +isOwned()) { + throw new FeedsValidationException(t('The address can not be saved because the user it should belong to is not found.')); + } + } + + /** + * Save an address. + */ + protected function entitySave($address) { + if ($address->isOwned()) { + $address->save(); + $address->feeds_item->entity_id = $address->getId(); + } + } + + /** + * Deletes a series of addresses. + */ + protected function entityDeleteMultiple($aids) { + foreach ($aids as $index => $aid) { + UcAddressesAddressBook::loadAddress($aid)->delete(); + } + } + + /** + * Declare default configuration. + */ + public function configDefaults() { + $defaults = parent::configDefaults(); + $defaults += array( + 'mark_as_default_billing' => TRUE, + 'mark_as_default_shipping' => TRUE, + ); + return $defaults; + } + + /** + * Overrides parent::configForm(). + */ + public function configForm(&$form_state) { + $form = parent::configForm($form_state); + unset($form['input_format']); + + // Setting to automatically make addresses default billing and default shipping. + $form['mark_as_default_billing'] = array( + '#type' => 'checkbox', + '#title' => t('Automatically mark addresses as default billing.'), + '#description' => t('If enabled, all users for which addresses are imported, get one default billing address.'), + '#default_value' => $this->config['mark_as_default_billing'], + ); + $form['mark_as_default_shipping'] = array( + '#type' => 'checkbox', + '#title' => t('Automatically mark addresses as default shipping.'), + '#description' => t('If enabled, all users for which addresses are imported, get one default shipping address.'), + '#default_value' => $this->config['mark_as_default_shipping'], + ); + + return $form; + } + + /** + * Override setTargetElement to operate on a target item that is an address. + */ + public function setTargetElement(FeedsSource $source, $target_address, $target_element, $value) { + switch ($target_element) { + case 'aid': + // Don't set. + return; + case 'user_name': + if ($user = user_load_by_name($value)) { + $target_address->setOwner($user->uid); + } + return; + case 'user_mail': + if ($user = user_load_by_mail($value)) { + $target_address->setOwner($user->uid); + } + return; + case 'uid': + $target_address->setOwner($value); + return; + } + + // Set target through the field handlers. + $handler = NULL; + $fieldname = $target_element; + $format = NULL; + // Check if the target element represents a existing address field. + if (UcAddressesSchemaAddress::fieldExists($fieldname)) { + $handler = uc_addresses_get_address_field_handler($target_address, $fieldname, 'feeds'); + } + else { + // Check if the target element represents a particular format of an existing address field. + $pieces = explode(':', $target_element); + if (count($pieces) >= 2) { + $fieldname = $pieces[0]; + array_shift($pieces); + $format = implode(':', $pieces); + if (UcAddressesSchemaAddress::fieldExists($fieldname)) { + // Get the handler for the field. + $handler = uc_addresses_get_address_field_handler($target_address, $fieldname, 'feeds'); + } + } + } + if ($handler instanceof UcAddressesFieldHandler) { + // Use the handler to set the right value. + $handler->mapValue($value, $format); + } + else { + parent::setTargetElement($source, $target_address, $target_element, $value); + } + } + + /** + * Return available mapping targets. + */ + public function getMappingTargets() { + $targets = parent::getMappingTargets(); + $address = UcAddressesAddress::newAddress(); + $fields = uc_addresses_get_address_field_handler_instances($address, 'feeds'); + foreach ($fields as $fieldname => $handler) { + $targets = array_merge($targets, $handler->getMappingTargets()); + } + + // Extra targets for user. + $targets['user_name'] = array( + 'name' => t('Username'), + 'description' => t('The Drupal username of the node author.'), + ); + $targets['user_mail'] = array( + 'name' => t('User email'), + 'description' => t('The email address of the node author.'), + ); + + // Set unique targets. + $targets['aid']['optional_unique'] = TRUE; + + return $targets; + } + + /** + * Get aid of an existing feed item address if available. + */ + protected function existingEntityId(FeedsSource $source, FeedsParserResult $result) { + if ($aid = parent::existingEntityId($source, $result)) { + return $aid; + } + + // Iterate through all unique targets and test whether they do already + // exist in the database. + foreach ($this->uniqueTargets($source, $result) as $target => $value) { + switch ($target) { + case 'aid': + $aid = db_select('uc_addresses') + ->fields('uc_addresses', array('aid')) + ->condition('aid', $value) + ->execute() + ->fetchField(); + break; + } + if ($aid) { + // Return with the first aid found. + return $aid; + } + } + return 0; + } + + /** + * Loads existing entity information and places it on $entity->feeds_item. + * + * @param $entity + * The entity object to load item info for. Id key must be present. + * + * @return + * TRUE if item info could be loaded, false if not. + */ + protected function loadItemInfo($entity) { + $entity_info = entity_get_info($this->entityType()); + $key = $entity_info['entity keys']['id']; + if ($item_info = feeds_item_info_load($this->entityType(), $entity->getField($key))) { + $entity->feeds_item = $item_info; + return TRUE; + } + return FALSE; + } + + /** + * Overrides FeedsProcessor::createLogMessage(). + * + * Creates a log message for when an exception occured during import. + * + * @param Exception $e + * The exception that was throwned during processing the item. + * @param $entity + * The entity object. + * @param $item + * The parser result for this entity. + * + * @return string + * The message to log. + */ + protected function createLogMessage(Exception $e, $entity, $item) { + include_once DRUPAL_ROOT . '/includes/utility.inc'; + $message = $e->getMessage(); + $message .= '

Original item

'; + $message .= '
' . drupal_var_export($item). '
'; + $message .= '

Address

'; + $message .= '
' . $entity->varExport() . '
'; + return $message; + } +} diff --git a/handlers/UcAddressesFieldHandler.class.php b/handlers/UcAddressesFieldHandler.class.php index 9b547c8..89a6dad 100644 --- a/handlers/UcAddressesFieldHandler.class.php +++ b/handlers/UcAddressesFieldHandler.class.php @@ -265,6 +265,39 @@ abstract class UcAddressesFieldHandler { } // ----------------------------------------------------------------------------- + // FEEDS + // Methods for integration with Feeds. + // ----------------------------------------------------------------------------- + + /** + * Returns supported mapping targets for Feeds. + * + * Is usually equal to the token info, but may differ in some cases. + * + * @return array + */ + public function getMappingTargets() { + return $this->getTokenInfo(); + } + + /** + * Set a fields value based on the output format. + * + * Field handlers that support specific output formats should + * override this method. + * + * @param mixed $value + * The formatted value. + * @param string $format + * (optional) The format in which the value exists. + * + * @see outputValue() + */ + public function mapValue($value, $format = '') { + $this->setValue($value); + } + + // ----------------------------------------------------------------------------- // OUTPUT // ----------------------------------------------------------------------------- diff --git a/handlers/ubercart.handlers.inc b/handlers/ubercart.handlers.inc index 1ad8cf4..5f6aca0 100644 --- a/handlers/ubercart.handlers.inc +++ b/handlers/ubercart.handlers.inc @@ -181,6 +181,50 @@ class UcAddressesUcZoneFieldHandler extends UcAddressesUcFieldHandler { } /** + * Returns supported mapping targets for Feeds. + * + * @return array + */ + public function getMappingTargets() { + $targets = parent::getMappingTargets(); + + // Specify clearer names and descriptions. + $targets['zone:zone_name']['name'] = t('Zone name'); + $targets['zone:zone_code']['name'] = t('Zone code'); + $targets['zone']['description'] = t('Zone ID as known to Ubercart'); + + return $targets; + } + + /** + * Set a fields value based on the output format. + * + * @param mixed $value + * The formatted value. + * @param string $format + * (optional) The format in which the value exists. + * + * @see outputValue() + */ + public function mapValue($value, $format = '') { + switch ($format) { + case 'zone_code': + case 'zone_name': + // Lookup zone data. + $zone_id = db_select('uc_zones', 'uc_zones') + ->condition($format, $value) + ->fields('uc_zones', array('zone_id')) + ->execute() + ->fetchField(); + if ($zone_id) { + $value = $zone_id; + } + break; + } + parent::mapValue($value, $format); + } + + /** * Returns an array of output formats for the zone field. * * @access public @@ -327,6 +371,67 @@ class UcAddressesUcCountryFieldHandler extends UcAddressesUcFieldHandler { } /** + * Returns supported mapping targets for Feeds. + * + * @return array + */ + public function getMappingTargets() { + $targets = parent::getMappingTargets(); + // Formats ending on "if" have no use for Feeds. + unset($targets['country:country_name_if']); + unset($targets['country:country_code2_if']); + unset($targets['country:country_code3_if']); + + // Specify clearer names and descriptions. + $targets['country:country_name']['name'] = t('Country name'); + $targets['country:country_code2']['name'] = t('Country code 2'); + $targets['country:country_code3']['name'] = t('Country code 3'); + $targets['country']['description'] = t('Country ID as known to Ubercart'); + $targets['country:country_name']['description'] = t('Name of the country in English'); + + return $targets; + } + + /** + * Sets a fields value based on the output format. + * + * @param mixed $value + * The formatted value. + * @param string $format + * (optional) The format in which the value exists. + * + * @see outputValue() + */ + public function mapValue($value, $format = '') { + switch ($format) { + case 'country_code2': + case 'country_code2_if': + $format = 'country_iso_code_2'; + break; + case 'country_code3': + case 'country_code3_if': + $format = 'country_iso_code_3'; + break; + } + switch ($format) { + case 'country_name': + case 'country_iso_code_2': + case 'country_iso_code_3': + // Lookup country data. + $country_id = db_select('uc_countries', 'uc_countries') + ->condition($format, $value) + ->fields('uc_countries', array('country_id')) + ->execute() + ->fetchField(); + if ($country_id) { + $value = $country_id; + } + break; + } + parent::mapValue($value, $format); + } + + /** * Returns an array of output formats country field. * * @access public diff --git a/uc_addresses.feeds.inc b/uc_addresses.feeds.inc new file mode 100644 index 0000000..89a7279 --- /dev/null +++ b/uc_addresses.feeds.inc @@ -0,0 +1,55 @@ +importer()->getConfig(); + $processor_config = $importer_config['processor']['config']; + + // Continue only if it were addresses that got imported. + if (empty($processor_config['bundle']) || $processor_config['bundle'] != 'uc_addresses') { + return; + } + + // Check if addresses may be set as default addresses as set in the config. + if (empty($processor_config['mark_as_default_billing']) && empty($processor_config['mark_as_default_shipping'])) { + // Addresses may not be marked as default. No reason to continue here. + return; + } + + $addressBooks = UcAddressesAddressBook::getAddressBooks(); + foreach ($addressBooks as $addressBook) { + if ($addressBook->isOwned()) { + foreach (uc_addresses_address_types() as $address_type) { + // Check for default addresses. + if (variable_get('uc_addresses_use_default_' . $address_type, TRUE) && $processor_config['mark_as_default_' . $address_type]) { + $address = $addressBook->getDefaultAddress($address_type); + if (!$address) { + // Mark one address as the default $address_type. + $addresses = $addressBook->getAddresses(); + $address = reset($addresses); + } + $addressBook->setAddressAsDefault($address, $address_type); + } + } + + // Save all changed addresses. + $addressBook->save(); + } + } +} diff --git a/uc_addresses.feeds_importer_default.inc b/uc_addresses.feeds_importer_default.inc new file mode 100644 index 0000000..055da60 --- /dev/null +++ b/uc_addresses.feeds_importer_default.inc @@ -0,0 +1,131 @@ +disabled = FALSE; /* Edit this to true to make a default feeds_importer disabled initially */ + $feeds_importer->api_version = 1; + $feeds_importer->id = 'uc_addresses'; + $feeds_importer->config = array( + 'name' => 'Ubercart Addresses address import', + 'description' => 'Import addresses from a CSV file.', + 'fetcher' => array( + 'plugin_key' => 'FeedsFileFetcher', + 'config' => array( + 'allowed_extensions' => 'txt csv tsv xml opml', + 'direct' => FALSE, + ), + ), + 'parser' => array( + 'plugin_key' => 'FeedsCSVParser', + 'config' => array( + 'delimiter' => ';', + 'no_headers' => 0, + ), + ), + 'processor' => array( + 'plugin_key' => 'FeedsUcAddressesProcessor', + 'config' => array( + 'mappings' => array( + array( + 'source' => 'user_name', + 'target' => 'user_name', + 'unique' => 0, + ), + array( + 'source' => 'first_name', + 'target' => 'first_name', + 'unique' => 0, + ), + array( + 'source' => 'last_name', + 'target' => 'last_name', + 'unique' => 0, + ), + array( + 'source' => 'phone', + 'target' => 'phone', + 'unique' => 0, + ), + array( + 'source' => 'company', + 'target' => 'company', + 'unique' => 0, + ), + array( + 'source' => 'street1', + 'target' => 'street1', + 'unique' => 0, + ), + array( + 'source' => 'street2', + 'target' => 'street2', + 'unique' => 0, + ), + array( + 'source' => 'city', + 'target' => 'city', + 'unique' => 0, + ), + array( + 'source' => 'zone', + 'target' => 'zone:zone_name', + 'unique' => 0, + ), + array( + 'source' => 'postal_code', + 'target' => 'postal_code', + 'unique' => 0, + ), + array( + 'source' => 'country', + 'target' => 'country:country_name', + 'unique' => 0, + ), + array( + 'source' => 'address_name', + 'target' => 'address_name', + 'unique' => 0, + ), + array( + 'source' => 'default_shipping', + 'target' => 'default_shipping', + 'unique' => 0, + ), + array( + 'source' => 'default_billing', + 'target' => 'default_billing', + 'unique' => 0, + ), + array( + 'source' => 'guid', + 'target' => 'guid', + 'unique' => 1, + ), + ), + 'update_existing' => '2', + 'mark_as_default_billing' => 1, + 'mark_as_default_shipping' => 1, + ), + ), + 'content_type' => '', + 'update' => 0, + 'import_period' => '-1', + 'expire_period' => 3600, + 'import_on_create' => 1, + 'process_in_background' => 0, + ); + $export['uc_addresses'] = $feeds_importer; + return $export; +} diff --git a/uc_addresses.info b/uc_addresses.info index eb046e8..f0b3cbc 100644 --- a/uc_addresses.info +++ b/uc_addresses.info @@ -32,6 +32,9 @@ files[] = views/uc_addresses_views_plugin_argument_address_access.inc files[] = views/uc_addresses_views_plugin_argument_user_address_access.inc files[] = views/uc_addresses_views_plugin_row_address_view.inc +; Feeds plugins +files[] = feeds/FeedsUcAddressesProcessor.inc + ; Test cases files[] = tests/UcAddressesTestCase.test files[] = tests/uc_addresses.addressbook.test diff --git a/uc_addresses.module b/uc_addresses.module index b71ff00..38f544a 100644 --- a/uc_addresses.module +++ b/uc_addresses.module @@ -1226,6 +1226,16 @@ function uc_addresses_ctools_plugin_api($owner, $api) { 'version' => 2, ); } + if ($owner == 'feeds' && $api == 'plugins') { + return array( + 'version' => 1, + ); + } + if ($owner == 'feeds' && $api == 'feeds_importer_default') { + return array( + 'version' => 1, + ); + } } /** @@ -1350,6 +1360,34 @@ function uc_addresses_views_api() { ); } +// --------------------------------------------------------------------------- +// FEEDS HOOKS +// --------------------------------------------------------------------------- + +/** + * Implements hook_feeds_plugins(). + * + * Declares the following: + * - Processor for Ubercart Addresses. + * + * @return array + * An array of Feeds plugins. + */ +function uc_addresses_feeds_plugins() { + $info = array(); + $info['FeedsUcAddressesProcessor'] = array( + 'name' => t('Ubercart Addresses processor'), + 'description' => t('Create and update Ubercart Addresses.'), + 'handler' => array( + 'parent' => 'FeedsProcessor', + 'class' => 'FeedsUcAddressesProcessor', + 'file' => 'FeedsUcAddressesProcessor.inc', + 'path' => drupal_get_path('module', 'uc_addresses') . '/feeds', + ), + ); + return $info; +} + // ----------------------------------------------------------------------------- // FORM ALTERS // -----------------------------------------------------------------------------