Skip to content
Snippets Groups Projects
FeedsProcessor.inc 40.3 KiB
Newer Older
  /**
   * Allows other modules to expose targets.
   *
   * @param array &$targets
   *   The existing target array.
   */
  protected function getHookTargets(array &$targets) {
    self::loadMappers();

    $entity_type = $this->entityType();
    $bundle = $this->bundle();
    $targets += module_invoke_all('feeds_processor_targets', $entity_type, $bundle);

    drupal_alter('feeds_processor_targets', $targets, $entity_type, $bundle);
  }

   * Set a concrete target element. Invoked from FeedsProcessor::map().
   *
   * @ingroup mappingapi
  public function setTargetElement(FeedsSource $source, $target_item, $target_element, $value) {
    switch ($target_element) {
      case 'url':
      case 'guid':
        $target_item->feeds_item->$target_element = $value;
        break;
      default:
        $target_item->$target_element = $value;
        break;
    }
   * Retrieve the target entity's existing id if available. Otherwise return 0.
   * @ingroup mappingapi
   *
   * @param FeedsSource $source
   *   The source information about this import.
   *   A FeedsParserResult object.
   *   The serial id of an entity if found, 0 otherwise.
  protected function existingEntityId(FeedsSource $source, FeedsParserResult $result) {

    $entity_id = 0;

    // Iterate through all unique targets and test whether they already exist in
    // the database.
    foreach ($this->uniqueTargets($source, $result) as $target => $value) {
      if ($target === 'guid' || $target === 'url') {
        $entity_id = db_select('feeds_item')
          ->fields('feeds_item', array('entity_id'))
          ->condition('feed_nid', $source->feed_nid)
          ->condition('entity_type', $this->entityType())
          ->condition('id', $source->id)
          ->condition($target, $value)
          ->execute()
          ->fetchField();
      if (!$entity_id && !empty($targets[$target]['unique_callbacks'])) {
        foreach ($targets[$target]['unique_callbacks'] as $callback) {
          if ($entity_id = call_user_func($callback, $source, $this->entityType(), $this->bundle(), $target, $value)) {
            // Stop at the first unique ID returned by a callback.
            break;
          }
        }
      }

      // Return with the content id found.
      if ($entity_id) {
        return $entity_id;
      }
    }
  /**
   * Utility function that iterates over a target array and retrieves all
   * sources that are unique.
   *
   *
   * @return
   *   An array where the keys are target field names and the values are the
   *   elements from the source item mapped to these targets.
   */
  protected function uniqueTargets(FeedsSource $source, FeedsParserResult $result) {
    $parser = feeds_importer($this->id)->parser;
    $targets = array();
    foreach ($this->getMappings() as $mapping) {
      if (!empty($mapping['unique'])) {
        // Invoke the parser's getSourceElement to retrieve the value for this
        // mapping's source.
        $targets[$mapping['target']] = $parser->getSourceElement($source, $result, $mapping['source']);

  /**
   * Adds Feeds specific information on $entity->feeds_item.
   *
   * @param $entity
   *   The entity object to be populated with new item info.
   * @param $feed_nid
   *   The feed nid of the source that produces this entity.
   * @param $hash
   *   The fingerprint of the source item.
   */
  protected function newItemInfo($entity, $feed_nid, $hash = '') {
    $entity->feeds_item = new stdClass();
    $entity->feeds_item->is_new = TRUE;
    $entity->feeds_item->entity_id = 0;
    $entity->feeds_item->entity_type = $this->entityType();
    $entity->feeds_item->id = $this->id;
    $entity->feeds_item->feed_nid = $feed_nid;
    $entity->feeds_item->imported = REQUEST_TIME;
    $entity->feeds_item->hash = $hash;
    $entity->feeds_item->url = '';
    $entity->feeds_item->guid = '';
  }

  /**
   * 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->$key)) {
      $entity->feeds_item = $item_info;
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Create MD5 hash of item and mappings array.
   *
   * Include mappings as a change in mappings may have an affect on the item
   * produced.
   *
   * @return string
   *   A hash is always returned, even when the item is empty, NULL or FALSE.
   */
  protected function hash($item) {
    $sources = feeds_importer($this->id)->parser->getMappingSourceList();
    $mapped_item = array_intersect_key($item, array_flip($sources));
    return hash('md5', serialize($mapped_item) . serialize($this->getMappings()));
   * Retrieves the MD5 hash of $entity_id from the database.
   *
   * @return string
   *   Empty string if no item is found, hash otherwise.
   */
  protected function getHash($entity_id) {
    if ($hash = db_query("SELECT hash FROM {feeds_item} WHERE entity_type = :type AND entity_id = :id", array(':type' => $this->entityType(), ':id' => $entity_id))->fetchField()) {
      // Return with the hash.
      return $hash;
    }
    return '';
  }
   * DEPRECATED: Creates a log message for exceptions during import.
   *
   * Don't use this method as it concatenates user variables into the log
   * message, which will pollute the locales_source table when the log message
   * is translated. Use ::createLogEntry instead.
   *
   * @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.
   *
   * @deprecated
   *   Use ::createLogEntry instead.
   */
  protected function createLogMessage(Exception $e, $entity, $item) {
    $message = $e->getMessage();
    $message .= '<h3>Original item</h3>';
    // $this->exportObjectVars() already runs check_plain() for us, so we can
    // concatenate here as is.
    $message .= '<pre>' . $this->exportObjectVars($item) . '</pre>';
    $message .= '<pre>' . $this->exportObjectVars($entity) . '</pre>';

   * Creates a log entry for when an exception occurred during import.
   *
   * @param Exception $e
   *   The exception that was throwned during processing the item.
   * @param object $entity
   *   The entity object.
   * @param array $item
   *   The parser result for this entity.
   *
   * @return array
   *   The message and arguments to log.
   */
  protected function createLogEntry(Exception $e, $entity, $item) {
    $message = '@exception';
    $message .= '<h3>Original item</h3>';
    $message .= '<pre>!item</pre>';
    $message .= '<h3>Entity</h3>';
    $message .= '<pre>!entity</pre>';
    $arguments = array(
      '@exception' => $e->getMessage(),
      // $this->exportObjectVars() already runs check_plain() for us, so we can
      // use the "!" placeholder.
      '!item' => $this->exportObjectVars($item),
      '!entity' => $this->exportObjectVars($entity),
    );

    return array($message, $arguments);
  }

  /**
   * Returns a string representation of an object or array for log messages.
   *
   * @param object|array $object
   *   The object to convert.
   *
   * @return string
   *   The sanitized string representation of the object.
   */
  protected function exportObjectVars($object) {
    include_once DRUPAL_ROOT . '/includes/utility.inc';

    $out = is_array($object) ? $object : get_object_vars($object);
    $out = array_filter($out, 'is_scalar');

    foreach ($out as $key => $value) {
      if (is_string($value)) {
        if (function_exists('mb_check_encoding') && !mb_check_encoding($value, 'UTF-8')) {
          $value = utf8_encode($value);
        }
        $out[$key] = truncate_utf8($value, 100, FALSE, TRUE);
      }
    }

    if (is_array($object)) {
      return check_plain(drupal_var_export($out));
    }

    return check_plain(drupal_var_export((object) $out));
  }

  /**
   * Overrides FeedsPlugin::dependencies().
   */
  public function dependencies() {
    $dependencies = parent::dependencies();

    // Find out which module defined the entity type.
    $info = $this->entityInfo();
    if (isset($info['module'])) {
      $dependencies[$info['module']] = $info['module'];
    }

    return $dependencies;
  }


class FeedsProcessorBundleNotDefined extends Exception {}

/**
 * Form after build callback for the field "update_existing".
 *
 * Adds descriptions to options of this field.
 */
function feeds_processor_config_form_update_existing_after_build($field) {
  $field[FEEDS_REPLACE_EXISTING]['#description'] =  t('Loads records directly from the database, bypassing the Entity API. Faster, but use with caution.');
  $field[FEEDS_UPDATE_EXISTING]['#description'] =  t('Loads complete @entities using the Entity API for full integration with other modules. Slower, but most reliable.', $field['#tokens']);
  return $field;
}