Projet

Général

Profil

Révision a192dc0b

Ajouté par Assos Assos il y a environ 8 ans

Weekly update of contrib modules

Voir les différences:

drupal7/sites/all/modules/feeds/plugins/FeedsProcessor.inc
5 5
 * Contains FeedsProcessor and related classes.
6 6
 */
7 7

  
8
// Insert mode for new items.
9
define('FEEDS_SKIP_NEW', 0);
10
define('FEEDS_INSERT_NEW', 1);
11

  
8 12
// Update mode for existing items.
9 13
define('FEEDS_SKIP_EXISTING', 0);
10 14
define('FEEDS_REPLACE_EXISTING', 1);
......
82 86
  /**
83 87
   * Create a new entity.
84 88
   *
85
   * @param $source
89
   * @param FeedsSource $source
86 90
   *   The feeds source that spawns this entity.
87 91
   *
88
   * @return
92
   * @return object
89 93
   *   A new entity object.
90 94
   */
91
  protected abstract function newEntity(FeedsSource $source);
95
  protected function newEntity(FeedsSource $source) {
96
    $entity = new stdClass();
97

  
98
    $info = $this->entityInfo();
99
    if (!empty($info['entity keys']['language'])) {
100
      $entity->{$info['entity keys']['language']} = $this->entityLanguage();
101
    }
102

  
103
    return $entity;
104
  }
92 105

  
93 106
  /**
94 107
   * Load an existing entity.
......
105 118
   *   existing ids first.
106 119
   */
107 120
  protected function entityLoad(FeedsSource $source, $entity_id) {
121
    $info = $this->entityInfo();
122

  
108 123
    if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) {
109 124
      $entities = entity_load($this->entityType(), array($entity_id));
110
      return reset($entities);
125
      $entity = reset($entities);
126
    }
127
    else {
128
      $args = array(':entity_id' => $entity_id);
129
      $table = db_escape_table($info['base table']);
130
      $key = db_escape_field($info['entity keys']['id']);
131
      $entity = db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject();
111 132
    }
112 133

  
113
    $info = $this->entityInfo();
114

  
115
    $args = array(':entity_id' => $entity_id);
116

  
117
    $table = db_escape_table($info['base table']);
118
    $key = db_escape_field($info['entity keys']['id']);
134
    if ($entity && !empty($info['entity keys']['language'])) {
135
      $entity->{$info['entity keys']['language']} = $this->entityLanguage();
136
    }
119 137

  
120
    return db_query("SELECT * FROM {" . $table . "} WHERE $key = :entity_id", $args)->fetchObject();
138
    return $entity;
121 139
  }
122 140

  
123 141
  /**
124
   * Validate an entity.
142
   * Validates an entity.
125 143
   *
126 144
   * @throws FeedsValidationException $e
127
   *   If validation fails.
145
   *   Thrown if validation fails.
128 146
   */
129
  protected function entityValidate($entity) {}
147
  protected function entityValidate($entity) {
148
    $info = $this->entityInfo();
149
    if (empty($info['entity keys']['language'])) {
150
      return;
151
    }
152

  
153
    // Ensure that a valid language is always set.
154
    $key = $info['entity keys']['language'];
155
    $languages = language_list('enabled');
156

  
157
    if (empty($entity->$key) || !isset($languages[1][$entity->$key])) {
158
      $entity->$key = $this->entityLanguage();
159
    }
160
  }
130 161

  
131 162
  /**
132 163
   * Access check for saving an enity.
......
162 193
   * 'label plural' ... the plural label of an entity type.
163 194
   */
164 195
  protected function entityInfo() {
165
    return entity_get_info($this->entityType());
196
    $info = entity_get_info($this->entityType());
197

  
198
    // Entity module has defined the plural label in "plural label" instead of
199
    // "label plural". So if "plural label" is defined, this will have priority
200
    // over "label plural".
201
    if (isset($info['plural label'])) {
202
      $info['label plural'] = $info['plural label'];
203
    }
204

  
205
    return $info;
206
  }
207

  
208
  /**
209
   * Returns the current language for entities.
210
   *
211
   * This checks if the configuration value is valid.
212
   *
213
   * @return string
214
   *   The current language code.
215
   */
216
  protected function entityLanguage() {
217
    if (!module_exists('locale')) {
218
      // language_list() may return languages even if the locale module is
219
      // disabled. See https://www.drupal.org/node/173227 why.
220
      // When the locale module is disabled, there are no selectable languages
221
      // in the UI, so the content should be imported in LANGUAGE_NONE.
222
      return LANGUAGE_NONE;
223
    }
224

  
225
    $languages = language_list('enabled');
226

  
227
    return isset($languages[1][$this->config['language']]) ? $this->config['language'] : LANGUAGE_NONE;
166 228
  }
167 229

  
168 230
  /**
......
183 245
      $this->initEntitiesToBeRemoved($source, $state);
184 246
    }
185 247

  
248
    $skip_new = $this->config['insert_new'] == FEEDS_SKIP_NEW;
249
    $skip_existing = $this->config['update_existing'] == FEEDS_SKIP_EXISTING;
250

  
186 251
    while ($item = $parser_result->shiftItem()) {
187 252

  
188 253
      // Check if this item already exists.
......
191 256
      if ($entity_id) {
192 257
        unset($state->removeList[$entity_id]);
193 258
      }
194
      $skip_existing = $this->config['update_existing'] == FEEDS_SKIP_EXISTING;
195 259

  
196 260
      module_invoke_all('feeds_before_update', $source, $item, $entity_id);
197 261

  
198
      // If it exists, and we are not updating, pass onto the next item.
199
      if ($entity_id && $skip_existing) {
262
      // If it exists, and we are not updating, or if it does not exist, and we
263
      // are not inserting, pass onto the next item.
264
      if (($entity_id && $skip_existing) || (!$entity_id && $skip_new)) {
200 265
        continue;
201 266
      }
202 267

  
203 268
      try {
204

  
205 269
        $hash = $this->hash($item);
206
        $changed = ($hash !== $this->getHash($entity_id));
207
        $force_update = $this->config['skip_hash_check'];
270
        $changed = $hash !== $this->getHash($entity_id);
208 271

  
209 272
        // Do not proceed if the item exists, has not changed, and we're not
210 273
        // forcing the update.
211
        if ($entity_id && !$changed && !$force_update) {
274
        if ($entity_id && !$changed && !$this->config['skip_hash_check']) {
212 275
          continue;
213 276
        }
214 277

  
......
357 420
  }
358 421

  
359 422
  /**
360
   * Initialize the array of entities to remove with all existing entities
361
   * previously imported from the source.
423
   * Initializes the list of entities to remove.
424
   *
425
   * This populates $state->removeList with all existing entities previously
426
   * imported from the source.
362 427
   *
363 428
   * @param FeedsSource $source
364 429
   *   Source information about this import.
......
367 432
   */
368 433
  protected function initEntitiesToBeRemoved(FeedsSource $source, FeedsState $state) {
369 434
    $state->removeList = array();
435

  
370 436
    // We fill it only if needed.
371
    if (!isset($this->config['update_non_existent']) || $this->config['update_non_existent'] == FEEDS_SKIP_NON_EXISTENT) {
437
    if ($this->config['update_non_existent'] == FEEDS_SKIP_NON_EXISTENT) {
372 438
      return;
373 439
    }
374
    // Build base select statement.
375
    $info = $this->entityInfo();
376
    $id_key = db_escape_field($info['entity keys']['id']);
377
    $select = db_select($info['base table'], 'e');
378
    $select->addField('e', $info['entity keys']['id'], 'entity_id');
379
    $select->join(
380
      'feeds_item',
381
      'fi',
382
      'e.' . $id_key . ' = fi.entity_id AND fi.entity_type = :entity_type', array(
383
        ':entity_type' => $this->entityType(),
384
    ));
385 440

  
386
    $select->condition('fi.id', $this->id);
387
    $select->condition('fi.feed_nid', $source->feed_nid);
388
    // No need to remove item again if same method of removal was already used.
389
    $select->condition('fi.hash', $this->config['update_non_existent'], '<>');
390
    $entities = $select->execute();
391
    // If not found on process, existing entities will be deleted.
392
    foreach ($entities as $entity) {
393
      // Obviously, items which are still included in the source feed will be
394
      // removed from this array when processed.
395
      $state->removeList[$entity->entity_id] = $entity->entity_id;
441
    // Get the full list of entities for this source.
442
    $entity_ids = db_select('feeds_item')
443
      ->fields('feeds_item', array('entity_id'))
444
      ->condition('entity_type', $this->entityType())
445
      ->condition('id', $this->id)
446
      ->condition('feed_nid', $source->feed_nid)
447
      ->condition('hash', $this->config['update_non_existent'], '<>')
448
      ->execute()
449
      ->fetchCol();
450

  
451
    if (!$entity_ids) {
452
      return;
396 453
    }
454

  
455
    $state->removeList = array_combine($entity_ids, $entity_ids);
397 456
  }
398 457

  
399 458
  /**
......
406 465
   */
407 466
  protected function clean(FeedsState $state) {
408 467
    // We clean only if needed.
409
    if (!isset($this->config['update_non_existent']) || $this->config['update_non_existent'] == FEEDS_SKIP_NON_EXISTENT) {
468
    if ($this->config['update_non_existent'] == FEEDS_SKIP_NON_EXISTENT) {
410 469
      return;
411 470
    }
412 471

  
413
    $total = count($state->removeList);
414
    if ($total) {
472
    if ($total = count($state->removeList)) {
415 473
      $this->entityDeleteMultiple($state->removeList);
416 474
      $state->deleted += $total;
417 475
    }
......
432 490
    $state = $source->state(FEEDS_PROCESS_CLEAR);
433 491

  
434 492
    // Build base select statement.
435
    $info = $this->entityInfo();
436
    $select = db_select($info['base table'], 'e');
437
    $select->addField('e', $info['entity keys']['id'], 'entity_id');
438
    $select->join(
439
      'feeds_item',
440
      'fi',
441
      "e.{$info['entity keys']['id']} = fi.entity_id AND fi.entity_type = '{$this->entityType()}'");
442
    $select->condition('fi.id', $this->id);
443
    $select->condition('fi.feed_nid', $source->feed_nid);
493
    $select = db_select('feeds_item')
494
      ->fields('feeds_item', array('entity_id'))
495
      ->condition('entity_type', $this->entityType())
496
      ->condition('id', $this->id)
497
      ->condition('feed_nid', $source->feed_nid);
444 498

  
445 499
    // If there is no total, query it.
446 500
    if (!$state->total) {
447
      $state->total = $select->countQuery()
448
        ->execute()
449
        ->fetchField();
501
      $state->total = $select->countQuery()->execute()->fetchField();
450 502
    }
451 503

  
452 504
    // Delete a batch of entities.
453
    $entities = $select->range(0, $this->getLimit())->execute();
454
    $entity_ids = array();
455
    foreach ($entities as $entity) {
456
      $entity_ids[$entity->entity_id] = $entity->entity_id;
457
    }
505
    $entity_ids = $select->range(0, $this->getLimit())->execute()->fetchCol();
458 506
    $this->entityDeleteMultiple($entity_ids);
459 507

  
460
    // Report progress, take into account that we may not have deleted as
461
    // many items as we have counted at first.
462
    if (count($entity_ids)) {
463
      $state->deleted += count($entity_ids);
508
    // Report progress, take into account that we may not have deleted as many
509
    // items as we have counted at first.
510
    if ($deleted_count = count($entity_ids)) {
511
      $state->deleted += $deleted_count;
464 512
      $state->progress($state->total, $state->deleted);
465 513
    }
466 514
    else {
......
469 517

  
470 518
    // Report results when done.
471 519
    if ($source->progressClearing() == FEEDS_BATCH_COMPLETE) {
520
      $info = $this->entityInfo();
521

  
472 522
      if ($state->deleted) {
473 523
        $message = format_plural(
474 524
          $state->deleted,
......
572 622
  protected function expiryQuery(FeedsSource $source, $time) {
573 623
    // Build base select statement.
574 624
    $info = $this->entityInfo();
575
    $id_key = db_escape_field($info['entity keys']['id']);
625
    $id_key = $info['entity keys']['id'];
576 626

  
577 627
    $select = db_select($info['base table'], 'e');
578
    $select->addField('e', $info['entity keys']['id'], 'entity_id');
579
    $select->join(
580
      'feeds_item',
581
      'fi',
582
      "e.$id_key = fi.entity_id AND fi.entity_type = :entity_type", array(
583
        ':entity_type' => $this->entityType(),
584
    ));
628
    $select->addField('e', $id_key);
629
    $select->join('feeds_item', 'fi', "e.$id_key = fi.entity_id");
630
    $select->condition('fi.entity_type', $this->entityType());
585 631
    $select->condition('fi.id', $this->id);
586 632
    $select->condition('fi.feed_nid', $source->feed_nid);
587 633

  
......
659 705
   */
660 706
  protected function map(FeedsSource $source, FeedsParserResult $result, $target_item = NULL) {
661 707
    $targets = $this->getCachedTargets();
708
    // Get fields for the entity type we are mapping to.
709
    $fields = field_info_instances($this->entityType(), $this->bundle());
662 710

  
663 711
    if (empty($target_item)) {
664 712
      $target_item = array();
......
667 715
    // Many mappers add to existing fields rather than replacing them. Hence we
668 716
    // need to clear target elements of each item before mapping in case we are
669 717
    // mapping on a prepopulated item such as an existing node.
670
    foreach ($this->config['mappings'] as $mapping) {
718
    foreach ($this->getMappings() as $mapping) {
671 719
      if (isset($targets[$mapping['target']]['real_target'])) {
672
        $target_item->{$targets[$mapping['target']]['real_target']} = NULL;
720
        $target_name = $targets[$mapping['target']]['real_target'];
721
      }
722
      else {
723
        $target_name = $mapping['target'];
724
      }
725

  
726
      // If the target is a field empty the value for the targeted language
727
      // only.
728
      // In all other cases, just empty the target completely.
729
      if (isset($fields[$target_name])) {
730
        // Empty the target for the specified language.
731
        $target_item->{$target_name}[$mapping['language']] = array();
673 732
      }
674 733
      else {
675
        $target_item->{$mapping['target']} = NULL;
734
        // Empty the whole target.
735
        $target_item->{$target_name} = NULL;
676 736
      }
677 737
    }
678 738

  
......
680 740
    // the parser's getSourceElement() method to retrieve the value of the
681 741
    // source element and pass it to the processor's setTargetElement() to stick
682 742
    // it on the right place of the target item.
683
    foreach ($this->config['mappings'] as $mapping) {
743
    foreach ($this->getMappings() as $mapping) {
684 744
      $value = $this->getSourceValue($source, $result, $mapping['source']);
685 745

  
686 746
      $this->mapToTarget($source, $mapping['target'], $target_item, $value, $mapping);
......
727 787
  protected function mapToTarget(FeedsSource $source, $target, &$target_item, $value, array $mapping) {
728 788
    $targets = $this->getCachedTargets();
729 789

  
730
    if (isset($targets[$target]['preprocess_callbacks'])) {
731
      foreach ($targets[$target]['preprocess_callbacks'] as $callback) {
732
        call_user_func_array($callback, array($source, $target_item, $target, &$mapping));
733
      }
734
    }
735

  
736 790
    // Map the source element's value to the target.
737 791
    // If the mapping specifies a callback method, use the callback instead of
738 792
    // setTargetElement().
......
770 824
    }
771 825
    return array(
772 826
      'mappings' => array(),
827
      'insert_new' => FEEDS_INSERT_NEW,
773 828
      'update_existing' => FEEDS_SKIP_EXISTING,
774 829
      'update_non_existent' => FEEDS_SKIP_NON_EXISTENT,
775 830
      'input_format' => NULL,
776 831
      'skip_hash_check' => FALSE,
777 832
      'bundle' => $bundle,
833
      'language' => LANGUAGE_NONE,
778 834
    );
779 835
  }
780 836

  
......
801 857
      );
802 858
    }
803 859

  
860
    if (module_exists('locale') && !empty($info['entity keys']['language'])) {
861
      $form['language'] = array(
862
        '#type' => 'select',
863
        '#options' => array(LANGUAGE_NONE => t('Language neutral')) + locale_language_list('name'),
864
        '#title' => t('Language'),
865
        '#required' => TRUE,
866
        '#default_value' => $this->config['language'],
867
      );
868
    }
869

  
804 870
    $tokens = array('@entities' => strtolower($info['label plural']));
805 871

  
872
    $form['insert_new'] = array(
873
      '#type' => 'radios',
874
      '#title' => t('Insert new @entities', $tokens),
875
      '#description' => t('New @entities will be determined using mappings that are a "unique target".', $tokens),
876
      '#options' => array(
877
        FEEDS_INSERT_NEW => t('Insert new @entities', $tokens),
878
        FEEDS_SKIP_NEW => t('Do not insert new @entities', $tokens),
879
      ),
880
      '#default_value' => $this->config['insert_new'],
881
    );
882

  
806 883
    $form['update_existing'] = array(
807 884
      '#type' => 'radios',
808 885
      '#title' => t('Update existing @entities', $tokens),
......
853 930
   * Get mappings.
854 931
   */
855 932
  public function getMappings() {
856
    return isset($this->config['mappings']) ? $this->config['mappings'] : array();
933
    $cache = &drupal_static('FeedsProcessor::getMappings', array());
934

  
935
    if (!isset($cache[$this->id])) {
936
      $mappings = $this->config['mappings'];
937
      $targets = $this->getCachedTargets();
938
      $languages = language_list('enabled');
939

  
940
      foreach ($mappings as &$mapping) {
941

  
942
        if (isset($targets[$mapping['target']]['preprocess_callbacks'])) {
943
          foreach ($targets[$mapping['target']]['preprocess_callbacks'] as $callback) {
944
            call_user_func_array($callback, array($targets[$mapping['target']], &$mapping));
945
          }
946
        }
947

  
948
        // Ensure there's always a language set.
949
        if (empty($mapping['language'])) {
950
          $mapping['language'] = LANGUAGE_NONE;
951
        }
952
        else {
953
          // Check if the configured language is available. If not, fallback to
954
          // LANGUAGE_NONE.
955
          if (!isset($languages[1][$mapping['language']])) {
956
            $mapping['language'] = LANGUAGE_NONE;
957
          }
958
        }
959
      }
960

  
961
      $cache[$this->id] = $mappings;
962
    }
963

  
964
    return $cache[$this->id];
857 965
  }
858 966

  
859 967
  /**
......
994 1102
  protected function uniqueTargets(FeedsSource $source, FeedsParserResult $result) {
995 1103
    $parser = feeds_importer($this->id)->parser;
996 1104
    $targets = array();
997
    foreach ($this->config['mappings'] as $mapping) {
1105
    foreach ($this->getMappings() as $mapping) {
998 1106
      if (!empty($mapping['unique'])) {
999 1107
        // Invoke the parser's getSourceElement to retrieve the value for this
1000 1108
        // mapping's source.
......
1052 1160
   * Include mappings as a change in mappings may have an affect on the item
1053 1161
   * produced.
1054 1162
   *
1055
   * @return Always returns a hash, even with empty, NULL, FALSE:
1056
   *  Empty arrays return 40cd750bba9870f18aada2478b24840a
1057
   *  Empty/NULL/FALSE strings return d41d8cd98f00b204e9800998ecf8427e
1163
   * @return string
1164
   *   A hash is always returned, even when the item is empty, NULL or FALSE.
1058 1165
   */
1059 1166
  protected function hash($item) {
1060
    return hash('md5', serialize($item) . serialize($this->config['mappings']));
1167
    $sources = feeds_importer($this->id)->parser->getMappingSourceList();
1168
    $mapped_item = array_intersect_key($item, array_flip($sources));
1169
    return hash('md5', serialize($mapped_item) . serialize($this->getMappings()));
1061 1170
  }
1062 1171

  
1063 1172
  /**
......
1108 1217
  }
1109 1218

  
1110 1219
  /**
1111
   * Creates a log entry for when an exception occured during import.
1220
   * Creates a log entry for when an exception occurred during import.
1112 1221
   *
1113 1222
   * @param Exception $e
1114 1223
   *   The exception that was throwned during processing the item.

Formats disponibles : Unified diff