Projet

Général

Profil

Paste
Télécharger (40,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / feeds / plugins / FeedsProcessor.inc @ 651307cd

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Contains FeedsProcessor and related classes.
6
 */
7
8 a192dc0b Assos Assos
// Insert mode for new items.
9
define('FEEDS_SKIP_NEW', 0);
10
define('FEEDS_INSERT_NEW', 1);
11
12 85ad3d82 Assos Assos
// Update mode for existing items.
13
define('FEEDS_SKIP_EXISTING', 0);
14
define('FEEDS_REPLACE_EXISTING', 1);
15
define('FEEDS_UPDATE_EXISTING', 2);
16 41cc1b08 Assos Assos
// Options for handling content in Drupal but not in source data.
17
define('FEEDS_SKIP_NON_EXISTENT', 'skip');
18
define('FEEDS_DELETE_NON_EXISTENT', 'delete');
19 85ad3d82 Assos Assos
20
// Default limit for creating items on a page load, not respected by all
21
// processors.
22
define('FEEDS_PROCESS_LIMIT', 50);
23
24
/**
25
 * Thrown if a validation fails.
26
 */
27
class FeedsValidationException extends Exception {}
28
29
/**
30
 * Thrown if a an access check fails.
31
 */
32
class FeedsAccessException extends Exception {}
33
34
/**
35
 * Abstract class, defines interface for processors.
36
 */
37
abstract class FeedsProcessor extends FeedsPlugin {
38
39
  /**
40
   * Implements FeedsPlugin::pluginType().
41
   */
42
  public function pluginType() {
43
    return 'processor';
44
  }
45
46
  /**
47
   * @defgroup entity_api_wrapper Entity API wrapper.
48
   */
49
50
  /**
51
   * Entity type this processor operates on.
52
   */
53
  public abstract function entityType();
54
55
  /**
56
   * Bundle type this processor operates on.
57
   *
58
   * Defaults to the entity type for entities that do not define bundles.
59
   *
60
   * @return string|NULL
61
   *   The bundle type this processor operates on, or NULL if it is undefined.
62
   */
63
  public function bundle() {
64
    return $this->config['bundle'];
65
  }
66
67
  /**
68
   * Provides a list of bundle options for use in select lists.
69
   *
70
   * @return array
71
   *   A keyed array of bundle => label.
72
   */
73
  public function bundleOptions() {
74
    $options = array();
75
    foreach (field_info_bundles($this->entityType()) as $bundle => $info) {
76
      if (!empty($info['label'])) {
77
        $options[$bundle] = $info['label'];
78
      }
79
      else {
80
        $options[$bundle] = $bundle;
81
      }
82
    }
83
    return $options;
84
  }
85
86
  /**
87
   * Create a new entity.
88
   *
89 a192dc0b Assos Assos
   * @param FeedsSource $source
90 85ad3d82 Assos Assos
   *   The feeds source that spawns this entity.
91
   *
92 a192dc0b Assos Assos
   * @return object
93 85ad3d82 Assos Assos
   *   A new entity object.
94
   */
95 a192dc0b Assos Assos
  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
  }
105 85ad3d82 Assos Assos
106
  /**
107
   * Load an existing entity.
108
   *
109
   * @param $source
110
   *   The feeds source that spawns this entity.
111
   * @param $entity_id
112
   *   The unique id of the entity that should be loaded.
113
   *
114
   * @return
115
   *   A new entity object.
116
   *
117
   * @todo We should be able to batch load these, if we found all of the
118
   *   existing ids first.
119
   */
120
  protected function entityLoad(FeedsSource $source, $entity_id) {
121 a192dc0b Assos Assos
    $info = $this->entityInfo();
122
123 85ad3d82 Assos Assos
    if ($this->config['update_existing'] == FEEDS_UPDATE_EXISTING) {
124
      $entities = entity_load($this->entityType(), array($entity_id));
125 a192dc0b Assos Assos
      $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();
132 85ad3d82 Assos Assos
    }
133
134 a192dc0b Assos Assos
    if ($entity && !empty($info['entity keys']['language'])) {
135
      $entity->{$info['entity keys']['language']} = $this->entityLanguage();
136
    }
137 85ad3d82 Assos Assos
138 a192dc0b Assos Assos
    return $entity;
139 85ad3d82 Assos Assos
  }
140
141
  /**
142 a192dc0b Assos Assos
   * Validates an entity.
143 85ad3d82 Assos Assos
   *
144
   * @throws FeedsValidationException $e
145 a192dc0b Assos Assos
   *   Thrown if validation fails.
146 85ad3d82 Assos Assos
   */
147 a192dc0b Assos Assos
  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
  }
161 85ad3d82 Assos Assos
162
  /**
163
   * Access check for saving an enity.
164
   *
165
   * @param $entity
166
   *   Entity to be saved.
167
   *
168
   * @throws FeedsAccessException $e
169
   *   If the access check fails.
170
   */
171
  protected function entitySaveAccess($entity) {}
172
173
  /**
174
   * Save an entity.
175
   *
176
   * @param $entity
177
   *   Entity to be saved.
178
   */
179
  protected abstract function entitySave($entity);
180
181
  /**
182
   * Delete a series of entities.
183
   *
184
   * @param $entity_ids
185
   *   Array of unique identity ids to be deleted.
186
   */
187
  protected abstract function entityDeleteMultiple($entity_ids);
188
189
  /**
190
   * Wrap entity_get_info() into a method so that extending classes can override
191
   * it and more entity information. Allowed additional keys:
192
   *
193
   * 'label plural' ... the plural label of an entity type.
194
   */
195
  protected function entityInfo() {
196 a192dc0b Assos Assos
    $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;
228 85ad3d82 Assos Assos
  }
229
230
  /**
231
   * @}
232
   */
233
234
  /**
235
   * Process the result of the parsing stage.
236
   *
237
   * @param FeedsSource $source
238
   *   Source information about this import.
239
   * @param FeedsParserResult $parser_result
240
   *   The result of the parsing stage.
241
   */
242
  public function process(FeedsSource $source, FeedsParserResult $parser_result) {
243
    $state = $source->state(FEEDS_PROCESS);
244 41cc1b08 Assos Assos
    if (!isset($state->removeList) && $parser_result->items) {
245
      $this->initEntitiesToBeRemoved($source, $state);
246
    }
247 85ad3d82 Assos Assos
248 a192dc0b Assos Assos
    $skip_new = $this->config['insert_new'] == FEEDS_SKIP_NEW;
249
    $skip_existing = $this->config['update_existing'] == FEEDS_SKIP_EXISTING;
250
251 85ad3d82 Assos Assos
    while ($item = $parser_result->shiftItem()) {
252
253
      // Check if this item already exists.
254
      $entity_id = $this->existingEntityId($source, $parser_result);
255 41cc1b08 Assos Assos
      // If it's included in the feed, it must not be removed on clean.
256
      if ($entity_id) {
257
        unset($state->removeList[$entity_id]);
258
      }
259 85ad3d82 Assos Assos
260
      module_invoke_all('feeds_before_update', $source, $item, $entity_id);
261
262 a192dc0b Assos Assos
      // 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)) {
265 85ad3d82 Assos Assos
        continue;
266
      }
267
268 41cc1b08 Assos Assos
      try {
269
        $hash = $this->hash($item);
270 a192dc0b Assos Assos
        $changed = $hash !== $this->getHash($entity_id);
271 85ad3d82 Assos Assos
272 41cc1b08 Assos Assos
        // Do not proceed if the item exists, has not changed, and we're not
273
        // forcing the update.
274 a192dc0b Assos Assos
        if ($entity_id && !$changed && !$this->config['skip_hash_check']) {
275 41cc1b08 Assos Assos
          continue;
276
        }
277 85ad3d82 Assos Assos
278
        // Load an existing entity.
279
        if ($entity_id) {
280
          $entity = $this->entityLoad($source, $entity_id);
281
282
          // The feeds_item table is always updated with the info for the most
283
          // recently processed entity. The only carryover is the entity_id.
284
          $this->newItemInfo($entity, $source->feed_nid, $hash);
285
          $entity->feeds_item->entity_id = $entity_id;
286
          $entity->feeds_item->is_new = FALSE;
287
        }
288
289
        // Build a new entity.
290
        else {
291
          $entity = $this->newEntity($source);
292
          $this->newItemInfo($entity, $source->feed_nid, $hash);
293
        }
294
295
        // Set property and field values.
296
        $this->map($source, $parser_result, $entity);
297
        $this->entityValidate($entity);
298
299
        // Allow modules to alter the entity before saving.
300
        module_invoke_all('feeds_presave', $source, $entity, $item, $entity_id);
301
        if (module_exists('rules')) {
302
          rules_invoke_event('feeds_import_'. $source->importer()->id, $entity);
303
        }
304
305
        // Enable modules to skip saving at all.
306
        if (!empty($entity->feeds_item->skip)) {
307
          continue;
308
        }
309
310
        // This will throw an exception on failure.
311
        $this->entitySaveAccess($entity);
312
        $this->entitySave($entity);
313
314
        // Allow modules to perform operations using the saved entity data.
315
        // $entity contains the updated entity after saving.
316
        module_invoke_all('feeds_after_save', $source, $entity, $item, $entity_id);
317
318
        // Track progress.
319
        if (empty($entity_id)) {
320
          $state->created++;
321
        }
322
        else {
323
          $state->updated++;
324
        }
325
      }
326
327
      // Something bad happened, log it.
328
      catch (Exception $e) {
329
        $state->failed++;
330
        drupal_set_message($e->getMessage(), 'warning');
331 41cc1b08 Assos Assos
        list($message, $arguments) = $this->createLogEntry($e, $entity, $item);
332
        $source->log('import', $message, $arguments, WATCHDOG_ERROR);
333 85ad3d82 Assos Assos
      }
334
    }
335
336
    // Set messages if we're done.
337
    if ($source->progressImporting() != FEEDS_BATCH_COMPLETE) {
338
      return;
339
    }
340 41cc1b08 Assos Assos
    // Remove not included items if needed.
341
    // It depends on the implementation of the clean() method what will happen
342
    // to items that were no longer in the source.
343
    $this->clean($state);
344 85ad3d82 Assos Assos
    $info = $this->entityInfo();
345
    $tokens = array(
346
      '@entity' => strtolower($info['label']),
347
      '@entities' => strtolower($info['label plural']),
348
    );
349
    $messages = array();
350
    if ($state->created) {
351
      $messages[] = array(
352
       'message' => format_plural(
353
          $state->created,
354
          'Created @number @entity.',
355
          'Created @number @entities.',
356
          array('@number' => $state->created) + $tokens
357
        ),
358
      );
359
    }
360
    if ($state->updated) {
361
      $messages[] = array(
362
       'message' => format_plural(
363
          $state->updated,
364
          'Updated @number @entity.',
365
          'Updated @number @entities.',
366
          array('@number' => $state->updated) + $tokens
367
        ),
368
      );
369
    }
370 41cc1b08 Assos Assos
    if ($state->unpublished) {
371
      $messages[] = array(
372
        'message' => format_plural(
373
            $state->unpublished,
374
            'Unpublished @number @entity.',
375
            'Unpublished @number @entities.',
376
            array('@number' => $state->unpublished) + $tokens
377
        ),
378
      );
379
    }
380
    if ($state->blocked) {
381
      $messages[] = array(
382
        'message' => format_plural(
383
          $state->blocked,
384
          'Blocked @number @entity.',
385
          'Blocked @number @entities.',
386
          array('@number' => $state->blocked) + $tokens
387
        ),
388
      );
389
    }
390
    if ($state->deleted) {
391
      $messages[] = array(
392
       'message' => format_plural(
393
          $state->deleted,
394
          'Removed @number @entity.',
395
          'Removed @number @entities.',
396
          array('@number' => $state->deleted) + $tokens
397
        ),
398
      );
399
    }
400 85ad3d82 Assos Assos
    if ($state->failed) {
401
      $messages[] = array(
402
       'message' => format_plural(
403
          $state->failed,
404
          'Failed importing @number @entity.',
405
          'Failed importing @number @entities.',
406
          array('@number' => $state->failed) + $tokens
407
        ),
408
        'level' => WATCHDOG_ERROR,
409
      );
410
    }
411
    if (empty($messages)) {
412
      $messages[] = array(
413
        'message' => t('There are no new @entities.', array('@entities' => strtolower($info['label plural']))),
414
      );
415
    }
416
    foreach ($messages as $message) {
417
      drupal_set_message($message['message']);
418
      $source->log('import', $message['message'], array(), isset($message['level']) ? $message['level'] : WATCHDOG_INFO);
419
    }
420
  }
421
422 41cc1b08 Assos Assos
  /**
423 a192dc0b Assos Assos
   * Initializes the list of entities to remove.
424
   *
425
   * This populates $state->removeList with all existing entities previously
426
   * imported from the source.
427 41cc1b08 Assos Assos
   *
428
   * @param FeedsSource $source
429
   *   Source information about this import.
430
   * @param FeedsState $state
431
   *   The FeedsState object for the given stage.
432
   */
433
  protected function initEntitiesToBeRemoved(FeedsSource $source, FeedsState $state) {
434
    $state->removeList = array();
435 a192dc0b Assos Assos
436 41cc1b08 Assos Assos
    // We fill it only if needed.
437 a192dc0b Assos Assos
    if ($this->config['update_non_existent'] == FEEDS_SKIP_NON_EXISTENT) {
438 41cc1b08 Assos Assos
      return;
439
    }
440
441 a192dc0b Assos Assos
    // 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;
453 41cc1b08 Assos Assos
    }
454 a192dc0b Assos Assos
455
    $state->removeList = array_combine($entity_ids, $entity_ids);
456 41cc1b08 Assos Assos
  }
457
458
  /**
459
   * Deletes entities which were not found during processing.
460
   *
461
   * @todo batch delete?
462
   *
463
   * @param FeedsState $state
464
   *   The FeedsState object for the given stage.
465
   */
466
  protected function clean(FeedsState $state) {
467
    // We clean only if needed.
468 7295e063 Assos Assos
    if ($this->config['update_non_existent'] != FEEDS_DELETE_NON_EXISTENT) {
469 41cc1b08 Assos Assos
      return;
470
    }
471
472 a192dc0b Assos Assos
    if ($total = count($state->removeList)) {
473 41cc1b08 Assos Assos
      $this->entityDeleteMultiple($state->removeList);
474
      $state->deleted += $total;
475
    }
476
  }
477
478 85ad3d82 Assos Assos
  /**
479
   * Remove all stored results or stored results up to a certain time for a
480
   * source.
481
   *
482
   * @param FeedsSource $source
483
   *   Source information for this expiry. Implementers should only delete items
484
   *   pertaining to this source. The preferred way of determining whether an
485
   *   item pertains to a certain souce is by using $source->feed_nid. It is the
486
   *   processor's responsibility to store the feed_nid of an imported item in
487
   *   the processing stage.
488
   */
489
  public function clear(FeedsSource $source) {
490
    $state = $source->state(FEEDS_PROCESS_CLEAR);
491
492
    // Build base select statement.
493 a192dc0b Assos Assos
    $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);
498 85ad3d82 Assos Assos
499
    // If there is no total, query it.
500
    if (!$state->total) {
501 a192dc0b Assos Assos
      $state->total = $select->countQuery()->execute()->fetchField();
502 85ad3d82 Assos Assos
    }
503
504
    // Delete a batch of entities.
505 a192dc0b Assos Assos
    $entity_ids = $select->range(0, $this->getLimit())->execute()->fetchCol();
506 85ad3d82 Assos Assos
    $this->entityDeleteMultiple($entity_ids);
507
508 a192dc0b Assos Assos
    // 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;
512 85ad3d82 Assos Assos
      $state->progress($state->total, $state->deleted);
513
    }
514
    else {
515
      $state->progress($state->total, $state->total);
516
    }
517
518
    // Report results when done.
519
    if ($source->progressClearing() == FEEDS_BATCH_COMPLETE) {
520 a192dc0b Assos Assos
      $info = $this->entityInfo();
521
522 85ad3d82 Assos Assos
      if ($state->deleted) {
523
        $message = format_plural(
524
          $state->deleted,
525
          'Deleted @number @entity',
526
          'Deleted @number @entities',
527
          array(
528
            '@number' => $state->deleted,
529
            '@entity' => strtolower($info['label']),
530
            '@entities' => strtolower($info['label plural']),
531
          )
532
        );
533
        $source->log('clear', $message, array(), WATCHDOG_INFO);
534
        drupal_set_message($message);
535
      }
536
      else {
537
        drupal_set_message(t('There are no @entities to be deleted.', array('@entities' => $info['label plural'])));
538
      }
539
    }
540
  }
541
542
  /*
543
   * Report number of items that can be processed per call.
544
   *
545
   * 0 means 'unlimited'.
546
   *
547
   * If a number other than 0 is given, Feeds parsers that support batching
548
   * will only deliver this limit to the processor.
549
   *
550
   * @see FeedsSource::getLimit()
551
   * @see FeedsCSVParser::parse()
552
   */
553
  public function getLimit() {
554
    return variable_get('feeds_process_limit', FEEDS_PROCESS_LIMIT);
555
  }
556
557
  /**
558 41cc1b08 Assos Assos
   * Deletes feed items older than REQUEST_TIME - $time.
559 85ad3d82 Assos Assos
   *
560 41cc1b08 Assos Assos
   * Do not invoke expire on a processor directly, but use
561
   * FeedsSource::expire() instead.
562
   *
563
   * @param FeedsSource $source
564
   *   The source to expire entities for.
565 85ad3d82 Assos Assos
   *
566
   * @param $time
567 41cc1b08 Assos Assos
   *   (optional) All items produced by this configuration that are older than
568
   *   REQUEST_TIME - $time should be deleted. If NULL, processor should use
569
   *   internal configuration. Defaults to NULL.
570 85ad3d82 Assos Assos
   *
571 41cc1b08 Assos Assos
   * @return float
572 85ad3d82 Assos Assos
   *   FEEDS_BATCH_COMPLETE if all items have been processed, a float between 0
573
   *   and 0.99* indicating progress otherwise.
574 41cc1b08 Assos Assos
   *
575
   * @see FeedsSource::expire()
576 85ad3d82 Assos Assos
   */
577 41cc1b08 Assos Assos
  public function expire(FeedsSource $source, $time = NULL) {
578
    $state = $source->state(FEEDS_PROCESS_EXPIRE);
579
580
    if ($time === NULL) {
581
      $time = $this->expiryTime();
582
    }
583
    if ($time == FEEDS_EXPIRE_NEVER) {
584
      return;
585
    }
586
587
    $select = $this->expiryQuery($source, $time);
588
589
    // If there is no total, query it.
590
    if (!$state->total) {
591
      $state->total = $select->countQuery()->execute()->fetchField();
592
    }
593
594
    // Delete a batch of entities.
595
    $entity_ids = $select->range(0, $this->getLimit())->execute()->fetchCol();
596
    if ($entity_ids) {
597
      $this->entityDeleteMultiple($entity_ids);
598
      $state->deleted += count($entity_ids);
599
      $state->progress($state->total, $state->deleted);
600
    }
601
    else {
602
      $state->progress($state->total, $state->total);
603
    }
604
  }
605
606
  /**
607
   * Returns a database query used to select entities to expire.
608
   *
609
   * Processor classes should override this method to set the age portion of the
610
   * query.
611
   *
612
   * @param FeedsSource $source
613
   *   The feed source.
614
   * @param int $time
615
   *   Delete entities older than this.
616
   *
617
   * @return SelectQuery
618
   *   A select query to execute.
619
   *
620
   * @see FeedsNodeProcessor::expiryQuery()
621
   */
622
  protected function expiryQuery(FeedsSource $source, $time) {
623
    // Build base select statement.
624
    $info = $this->entityInfo();
625 a192dc0b Assos Assos
    $id_key = $info['entity keys']['id'];
626 41cc1b08 Assos Assos
627
    $select = db_select($info['base table'], 'e');
628 a192dc0b Assos Assos
    $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());
631 41cc1b08 Assos Assos
    $select->condition('fi.id', $this->id);
632
    $select->condition('fi.feed_nid', $source->feed_nid);
633
634
    return $select;
635 85ad3d82 Assos Assos
  }
636
637
  /**
638
   * Counts the number of items imported by this processor.
639
   */
640
  public function itemCount(FeedsSource $source) {
641
    return db_query("SELECT count(*) FROM {feeds_item} WHERE id = :id AND entity_type = :entity_type AND feed_nid = :feed_nid", array(':id' => $this->id, ':entity_type' => $this->entityType(), ':feed_nid' => $source->feed_nid))->fetchField();
642
  }
643
644 41cc1b08 Assos Assos
  /**
645
   * Returns a statically cached version of the target mappings.
646
   *
647
   * @return array
648
   *   The targets for this importer.
649
   */
650
  protected function getCachedTargets() {
651
    $targets = &drupal_static('FeedsProcessor::getCachedTargets', array());
652
653
    if (!isset($targets[$this->id])) {
654
      $targets[$this->id] = $this->getMappingTargets();
655
    }
656
657
    return $targets[$this->id];
658
  }
659
660
  /**
661
   * Returns a statically cached version of the source mappings.
662
   *
663
   * @return array
664
   *   The sources for this importer.
665
   */
666
  protected function getCachedSources() {
667
    $sources = &drupal_static('FeedsProcessor::getCachedSources', array());
668
669
    if (!isset($sources[$this->id])) {
670
671
      $sources[$this->id] = feeds_importer($this->id)->parser->getMappingSources();
672
673
      if (is_array($sources[$this->id])) {
674
        foreach ($sources[$this->id] as $source_key => $source) {
675
          if (empty($source['callback']) || !is_callable($source['callback'])) {
676
            unset($sources[$this->id][$source_key]['callback']);
677
          }
678
        }
679
      }
680
    }
681
682
    return $sources[$this->id];
683
  }
684
685 85ad3d82 Assos Assos
  /**
686
   * Execute mapping on an item.
687
   *
688
   * This method encapsulates the central mapping functionality. When an item is
689
   * processed, it is passed through map() where the properties of $source_item
690
   * are mapped onto $target_item following the processor's mapping
691
   * configuration.
692
   *
693
   * For each mapping FeedsParser::getSourceElement() is executed to retrieve
694
   * the source element, then FeedsProcessor::setTargetElement() is invoked
695
   * to populate the target item properly. Alternatively a
696
   * hook_x_targets_alter() may have specified a callback for a mapping target
697
   * in which case the callback is asked to populate the target item instead of
698
   * FeedsProcessor::setTargetElement().
699
   *
700
   * @ingroup mappingapi
701
   *
702
   * @see hook_feeds_parser_sources_alter()
703 41cc1b08 Assos Assos
   * @see hook_feeds_processor_targets()
704
   * @see hook_feeds_processor_targets_alter()
705 85ad3d82 Assos Assos
   */
706
  protected function map(FeedsSource $source, FeedsParserResult $result, $target_item = NULL) {
707 41cc1b08 Assos Assos
    $targets = $this->getCachedTargets();
708 a192dc0b Assos Assos
    // Get fields for the entity type we are mapping to.
709
    $fields = field_info_instances($this->entityType(), $this->bundle());
710 85ad3d82 Assos Assos
711
    if (empty($target_item)) {
712
      $target_item = array();
713
    }
714
715
    // Many mappers add to existing fields rather than replacing them. Hence we
716
    // need to clear target elements of each item before mapping in case we are
717
    // mapping on a prepopulated item such as an existing node.
718 a192dc0b Assos Assos
    foreach ($this->getMappings() as $mapping) {
719 41cc1b08 Assos Assos
      if (isset($targets[$mapping['target']]['real_target'])) {
720 a192dc0b Assos Assos
        $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();
732 85ad3d82 Assos Assos
      }
733 41cc1b08 Assos Assos
      else {
734 a192dc0b Assos Assos
        // Empty the whole target.
735
        $target_item->{$target_name} = NULL;
736 85ad3d82 Assos Assos
      }
737
    }
738
739 41cc1b08 Assos Assos
    // This is where the actual mapping happens: For every mapping we invoke
740
    // the parser's getSourceElement() method to retrieve the value of the
741
    // source element and pass it to the processor's setTargetElement() to stick
742
    // it on the right place of the target item.
743 a192dc0b Assos Assos
    foreach ($this->getMappings() as $mapping) {
744 41cc1b08 Assos Assos
      $value = $this->getSourceValue($source, $result, $mapping['source']);
745
746
      $this->mapToTarget($source, $mapping['target'], $target_item, $value, $mapping);
747
    }
748
749
    return $target_item;
750
  }
751 85ad3d82 Assos Assos
752 41cc1b08 Assos Assos
  /**
753
   * Returns the values from the parser, or callback.
754
   *
755
   * @param FeedsSource $source
756
   *   The feed source.
757
   * @param FeedsParserResult $result
758
   *   The parser result.
759
   * @param string $source_key
760
   *   The current key being processed.
761
   *
762
   * @return mixed
763
   *   A value, or a list of values.
764
   */
765
  protected function getSourceValue(FeedsSource $source, FeedsParserResult $result, $source_key) {
766
    $sources = $this->getCachedSources();
767
768
    if (isset($sources[$source_key]['callback'])) {
769
      return call_user_func($sources[$source_key]['callback'], $source, $result, $source_key);
770
    }
771
772
    return feeds_importer($this->id)->parser->getSourceElement($source, $result, $source_key);
773
  }
774
775
  /**
776
   * Maps values onto the target item.
777
   *
778
   * @param FeedsSource $source
779
   *   The feed source.
780
   * @param mixed &$target_item
781
   *   The target item to apply values into.
782
   * @param mixed $value
783
   *   A value, or a list of values.
784
   * @param array $mapping
785
   *   The mapping configuration.
786
   */
787
  protected function mapToTarget(FeedsSource $source, $target, &$target_item, $value, array $mapping) {
788
    $targets = $this->getCachedTargets();
789
790
    // Map the source element's value to the target.
791
    // If the mapping specifies a callback method, use the callback instead of
792
    // setTargetElement().
793
    if (isset($targets[$target]['callback'])) {
794
795
      // All target callbacks expect an array.
796
      if (!is_array($value)) {
797
        $value = array($value);
798 85ad3d82 Assos Assos
      }
799 41cc1b08 Assos Assos
800
      call_user_func($targets[$target]['callback'], $source, $target_item, $target, $value, $mapping);
801
    }
802
803
    else {
804
      $this->setTargetElement($source, $target_item, $target, $value, $mapping);
805 85ad3d82 Assos Assos
    }
806
  }
807
808
  /**
809
   * Per default, don't support expiry. If processor supports expiry of imported
810
   * items, return the time after which items should be removed.
811
   */
812
  public function expiryTime() {
813
    return FEEDS_EXPIRE_NEVER;
814
  }
815
816
  /**
817
   * Declare default configuration.
818
   */
819
  public function configDefaults() {
820
    $info = $this->entityInfo();
821
    $bundle = NULL;
822
    if (empty($info['entity keys']['bundle'])) {
823
      $bundle = $this->entityType();
824
    }
825
    return array(
826
      'mappings' => array(),
827 a192dc0b Assos Assos
      'insert_new' => FEEDS_INSERT_NEW,
828 85ad3d82 Assos Assos
      'update_existing' => FEEDS_SKIP_EXISTING,
829 41cc1b08 Assos Assos
      'update_non_existent' => FEEDS_SKIP_NON_EXISTENT,
830 85ad3d82 Assos Assos
      'input_format' => NULL,
831
      'skip_hash_check' => FALSE,
832
      'bundle' => $bundle,
833 a192dc0b Assos Assos
      'language' => LANGUAGE_NONE,
834 85ad3d82 Assos Assos
    );
835
  }
836
837
  /**
838
   * Overrides parent::configForm().
839
   */
840
  public function configForm(&$form_state) {
841
    $info = $this->entityInfo();
842
    $form = array();
843
844
    if (!empty($info['entity keys']['bundle'])) {
845
      $form['bundle'] = array(
846
        '#type' => 'select',
847
        '#options' => $this->bundleOptions(),
848
        '#title' => !empty($info['bundle name']) ? $info['bundle name'] : t('Bundle'),
849
        '#required' => TRUE,
850
        '#default_value' => $this->bundle(),
851
      );
852
    }
853
    else {
854
      $form['bundle'] = array(
855
        '#type' => 'value',
856
        '#value' => $this->entityType(),
857
      );
858
    }
859
860 a192dc0b Assos Assos
    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
870 85ad3d82 Assos Assos
    $tokens = array('@entities' => strtolower($info['label plural']));
871
872 a192dc0b Assos Assos
    $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
883 85ad3d82 Assos Assos
    $form['update_existing'] = array(
884
      '#type' => 'radios',
885
      '#title' => t('Update existing @entities', $tokens),
886 7295e063 Assos Assos
      '#description' => t('Existing @entities will be determined using mappings that are a "unique target".', $tokens),
887 85ad3d82 Assos Assos
      '#options' => array(
888
        FEEDS_SKIP_EXISTING => t('Do not update existing @entities', $tokens),
889
        FEEDS_REPLACE_EXISTING => t('Replace existing @entities', $tokens),
890
        FEEDS_UPDATE_EXISTING => t('Update existing @entities', $tokens),
891
      ),
892
      '#default_value' => $this->config['update_existing'],
893 7295e063 Assos Assos
      '#after_build' => array('feeds_processor_config_form_update_existing_after_build'),
894
      '#tokens' => $tokens,
895 85ad3d82 Assos Assos
    );
896
    global $user;
897
    $formats = filter_formats($user);
898
    foreach ($formats as $format) {
899
      $format_options[$format->format] = $format->name;
900
    }
901
    $form['skip_hash_check'] = array(
902
      '#type' => 'checkbox',
903
      '#title' => t('Skip hash check'),
904
      '#description' => t('Force update of items even if item source data did not change.'),
905
      '#default_value' => $this->config['skip_hash_check'],
906
    );
907
    $form['input_format'] = array(
908
      '#type' => 'select',
909
      '#title' => t('Text format'),
910 41cc1b08 Assos Assos
      '#description' => t('Select the default input format for the text fields of the nodes to be created.'),
911 85ad3d82 Assos Assos
      '#options' => $format_options,
912
      '#default_value' => isset($this->config['input_format']) ? $this->config['input_format'] : 'plain_text',
913
      '#required' => TRUE,
914
    );
915
916 41cc1b08 Assos Assos
    $form['update_non_existent'] = array(
917
      '#type' => 'radios',
918
      '#title' => t('Action to take when previously imported @entities are missing in the feed', $tokens),
919
      '#description' => t('Select how @entities previously imported and now missing in the feed should be updated.', $tokens),
920
      '#options' => array(
921
        FEEDS_SKIP_NON_EXISTENT => t('Skip non-existent @entities', $tokens),
922
        FEEDS_DELETE_NON_EXISTENT => t('Delete non-existent @entities', $tokens),
923
      ),
924
      '#default_value' => $this->config['update_non_existent'],
925
    );
926
927 85ad3d82 Assos Assos
    return $form;
928
  }
929
930
  /**
931
   * Get mappings.
932
   */
933
  public function getMappings() {
934 a192dc0b Assos Assos
    $cache = &drupal_static('FeedsProcessor::getMappings', array());
935
936
    if (!isset($cache[$this->id])) {
937
      $mappings = $this->config['mappings'];
938
      $targets = $this->getCachedTargets();
939
      $languages = language_list('enabled');
940
941
      foreach ($mappings as &$mapping) {
942
943
        if (isset($targets[$mapping['target']]['preprocess_callbacks'])) {
944
          foreach ($targets[$mapping['target']]['preprocess_callbacks'] as $callback) {
945
            call_user_func_array($callback, array($targets[$mapping['target']], &$mapping));
946
          }
947
        }
948
949
        // Ensure there's always a language set.
950
        if (empty($mapping['language'])) {
951
          $mapping['language'] = LANGUAGE_NONE;
952
        }
953
        else {
954
          // Check if the configured language is available. If not, fallback to
955
          // LANGUAGE_NONE.
956
          if (!isset($languages[1][$mapping['language']])) {
957
            $mapping['language'] = LANGUAGE_NONE;
958
          }
959
        }
960
      }
961
962
      $cache[$this->id] = $mappings;
963
    }
964
965
    return $cache[$this->id];
966 85ad3d82 Assos Assos
  }
967
968
  /**
969
   * Declare possible mapping targets that this processor exposes.
970
   *
971
   * @ingroup mappingapi
972
   *
973
   * @return
974
   *   An array of mapping targets. Keys are paths to targets
975
   *   separated by ->, values are TRUE if target can be unique,
976
   *   FALSE otherwise.
977
   */
978
  public function getMappingTargets() {
979
980
    // The bundle has not been selected.
981
    if (!$this->bundle()) {
982
      $info = $this->entityInfo();
983
      $bundle_name = !empty($info['bundle name']) ? drupal_strtolower($info['bundle name']) : t('bundle');
984
      $plugin_key = feeds_importer($this->id)->config['processor']['plugin_key'];
985
      $url = url('admin/structure/feeds/' . $this->id . '/settings/' . $plugin_key);
986
      drupal_set_message(t('Please <a href="@url">select a @bundle_name</a>.', array('@url' => $url, '@bundle_name' => $bundle_name)), 'warning', FALSE);
987
    }
988
989
    return array(
990
      'url' => array(
991
        'name' => t('URL'),
992
        'description' => t('The external URL of the item. E. g. the feed item URL in the case of a syndication feed. May be unique.'),
993
        'optional_unique' => TRUE,
994
      ),
995
      'guid' => array(
996
        'name' => t('GUID'),
997
        'description' => t('The globally unique identifier of the item. E. g. the feed item GUID in the case of a syndication feed. May be unique.'),
998
        'optional_unique' => TRUE,
999
      ),
1000
    );
1001
  }
1002
1003 41cc1b08 Assos Assos
  /**
1004
   * Allows other modules to expose targets.
1005
   *
1006
   * @param array &$targets
1007
   *   The existing target array.
1008
   */
1009
  protected function getHookTargets(array &$targets) {
1010
    self::loadMappers();
1011
1012
    $entity_type = $this->entityType();
1013
    $bundle = $this->bundle();
1014
    $targets += module_invoke_all('feeds_processor_targets', $entity_type, $bundle);
1015
1016
    drupal_alter('feeds_processor_targets', $targets, $entity_type, $bundle);
1017
  }
1018
1019 85ad3d82 Assos Assos
  /**
1020
   * Set a concrete target element. Invoked from FeedsProcessor::map().
1021
   *
1022
   * @ingroup mappingapi
1023
   */
1024
  public function setTargetElement(FeedsSource $source, $target_item, $target_element, $value) {
1025
    switch ($target_element) {
1026
      case 'url':
1027
      case 'guid':
1028
        $target_item->feeds_item->$target_element = $value;
1029
        break;
1030 41cc1b08 Assos Assos
1031 85ad3d82 Assos Assos
      default:
1032
        $target_item->$target_element = $value;
1033
        break;
1034
    }
1035
  }
1036
1037
  /**
1038
   * Retrieve the target entity's existing id if available. Otherwise return 0.
1039
   *
1040
   * @ingroup mappingapi
1041
   *
1042
   * @param FeedsSource $source
1043
   *   The source information about this import.
1044 41cc1b08 Assos Assos
   * @param FeedsParserResult $result
1045 85ad3d82 Assos Assos
   *   A FeedsParserResult object.
1046
   *
1047 41cc1b08 Assos Assos
   * @return int
1048 85ad3d82 Assos Assos
   *   The serial id of an entity if found, 0 otherwise.
1049
   */
1050
  protected function existingEntityId(FeedsSource $source, FeedsParserResult $result) {
1051 41cc1b08 Assos Assos
    $targets = $this->getCachedTargets();
1052
1053
    $entity_id = 0;
1054
1055
    // Iterate through all unique targets and test whether they already exist in
1056
    // the database.
1057 85ad3d82 Assos Assos
    foreach ($this->uniqueTargets($source, $result) as $target => $value) {
1058 41cc1b08 Assos Assos
      if ($target === 'guid' || $target === 'url') {
1059
        $entity_id = db_select('feeds_item')
1060
          ->fields('feeds_item', array('entity_id'))
1061
          ->condition('feed_nid', $source->feed_nid)
1062
          ->condition('entity_type', $this->entityType())
1063
          ->condition('id', $source->id)
1064
          ->condition($target, $value)
1065
          ->execute()
1066
          ->fetchField();
1067 85ad3d82 Assos Assos
      }
1068 41cc1b08 Assos Assos
1069
      if (!$entity_id && !empty($targets[$target]['unique_callbacks'])) {
1070
        if (!is_array($value)) {
1071
          $value = array($value);
1072
        }
1073
1074
        foreach ($targets[$target]['unique_callbacks'] as $callback) {
1075
          if ($entity_id = call_user_func($callback, $source, $this->entityType(), $this->bundle(), $target, $value)) {
1076
            // Stop at the first unique ID returned by a callback.
1077
            break;
1078
          }
1079
        }
1080
      }
1081
1082
      // Return with the content id found.
1083
      if ($entity_id) {
1084 85ad3d82 Assos Assos
        return $entity_id;
1085
      }
1086
    }
1087 41cc1b08 Assos Assos
1088
    return $entity_id;
1089 85ad3d82 Assos Assos
  }
1090
1091
1092
  /**
1093
   * Utility function that iterates over a target array and retrieves all
1094
   * sources that are unique.
1095
   *
1096
   * @param $batch
1097
   *   A FeedsImportBatch.
1098
   *
1099
   * @return
1100
   *   An array where the keys are target field names and the values are the
1101
   *   elements from the source item mapped to these targets.
1102
   */
1103
  protected function uniqueTargets(FeedsSource $source, FeedsParserResult $result) {
1104
    $parser = feeds_importer($this->id)->parser;
1105
    $targets = array();
1106 a192dc0b Assos Assos
    foreach ($this->getMappings() as $mapping) {
1107 85ad3d82 Assos Assos
      if (!empty($mapping['unique'])) {
1108
        // Invoke the parser's getSourceElement to retrieve the value for this
1109
        // mapping's source.
1110
        $targets[$mapping['target']] = $parser->getSourceElement($source, $result, $mapping['source']);
1111
      }
1112
    }
1113
    return $targets;
1114
  }
1115
1116
  /**
1117
   * Adds Feeds specific information on $entity->feeds_item.
1118
   *
1119
   * @param $entity
1120
   *   The entity object to be populated with new item info.
1121
   * @param $feed_nid
1122
   *   The feed nid of the source that produces this entity.
1123
   * @param $hash
1124
   *   The fingerprint of the source item.
1125
   */
1126
  protected function newItemInfo($entity, $feed_nid, $hash = '') {
1127
    $entity->feeds_item = new stdClass();
1128
    $entity->feeds_item->is_new = TRUE;
1129
    $entity->feeds_item->entity_id = 0;
1130
    $entity->feeds_item->entity_type = $this->entityType();
1131
    $entity->feeds_item->id = $this->id;
1132
    $entity->feeds_item->feed_nid = $feed_nid;
1133
    $entity->feeds_item->imported = REQUEST_TIME;
1134
    $entity->feeds_item->hash = $hash;
1135
    $entity->feeds_item->url = '';
1136
    $entity->feeds_item->guid = '';
1137
  }
1138
1139
  /**
1140
   * Loads existing entity information and places it on $entity->feeds_item.
1141
   *
1142
   * @param $entity
1143
   *   The entity object to load item info for. Id key must be present.
1144
   *
1145
   * @return
1146
   *   TRUE if item info could be loaded, false if not.
1147
   */
1148
  protected function loadItemInfo($entity) {
1149
    $entity_info = entity_get_info($this->entityType());
1150
    $key = $entity_info['entity keys']['id'];
1151
    if ($item_info = feeds_item_info_load($this->entityType(), $entity->$key)) {
1152
      $entity->feeds_item = $item_info;
1153
      return TRUE;
1154
    }
1155
    return FALSE;
1156
  }
1157
1158
  /**
1159
   * Create MD5 hash of item and mappings array.
1160
   *
1161
   * Include mappings as a change in mappings may have an affect on the item
1162
   * produced.
1163
   *
1164 a192dc0b Assos Assos
   * @return string
1165
   *   A hash is always returned, even when the item is empty, NULL or FALSE.
1166 85ad3d82 Assos Assos
   */
1167
  protected function hash($item) {
1168 a192dc0b Assos Assos
    $sources = feeds_importer($this->id)->parser->getMappingSourceList();
1169
    $mapped_item = array_intersect_key($item, array_flip($sources));
1170
    return hash('md5', serialize($mapped_item) . serialize($this->getMappings()));
1171 85ad3d82 Assos Assos
  }
1172
1173
  /**
1174
   * Retrieves the MD5 hash of $entity_id from the database.
1175
   *
1176
   * @return string
1177
   *   Empty string if no item is found, hash otherwise.
1178
   */
1179
  protected function getHash($entity_id) {
1180
1181
    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()) {
1182
      // Return with the hash.
1183
      return $hash;
1184
    }
1185
    return '';
1186
  }
1187
1188
  /**
1189 41cc1b08 Assos Assos
   * DEPRECATED: Creates a log message for exceptions during import.
1190
   *
1191
   * Don't use this method as it concatenates user variables into the log
1192
   * message, which will pollute the locales_source table when the log message
1193
   * is translated. Use ::createLogEntry instead.
1194 85ad3d82 Assos Assos
   *
1195
   * @param Exception $e
1196
   *   The exception that was throwned during processing the item.
1197
   * @param $entity
1198
   *   The entity object.
1199
   * @param $item
1200
   *   The parser result for this entity.
1201
   *
1202
   * @return string
1203
   *   The message to log.
1204 41cc1b08 Assos Assos
   *
1205
   * @deprecated
1206
   *   Use ::createLogEntry instead.
1207 85ad3d82 Assos Assos
   */
1208
  protected function createLogMessage(Exception $e, $entity, $item) {
1209
    $message = $e->getMessage();
1210
    $message .= '<h3>Original item</h3>';
1211 41cc1b08 Assos Assos
    // $this->exportObjectVars() already runs check_plain() for us, so we can
1212
    // concatenate here as is.
1213
    $message .= '<pre>' . $this->exportObjectVars($item) . '</pre>';
1214 85ad3d82 Assos Assos
    $message .= '<h3>Entity</h3>';
1215 41cc1b08 Assos Assos
    $message .= '<pre>' . $this->exportObjectVars($entity) . '</pre>';
1216
1217 85ad3d82 Assos Assos
    return $message;
1218
  }
1219
1220 41cc1b08 Assos Assos
  /**
1221 a192dc0b Assos Assos
   * Creates a log entry for when an exception occurred during import.
1222 41cc1b08 Assos Assos
   *
1223
   * @param Exception $e
1224
   *   The exception that was throwned during processing the item.
1225
   * @param object $entity
1226
   *   The entity object.
1227
   * @param array $item
1228
   *   The parser result for this entity.
1229
   *
1230
   * @return array
1231
   *   The message and arguments to log.
1232
   */
1233
  protected function createLogEntry(Exception $e, $entity, $item) {
1234
    $message = '@exception';
1235
    $message .= '<h3>Original item</h3>';
1236
    $message .= '<pre>!item</pre>';
1237
    $message .= '<h3>Entity</h3>';
1238
    $message .= '<pre>!entity</pre>';
1239
    $arguments = array(
1240
      '@exception' => $e->getMessage(),
1241
      // $this->exportObjectVars() already runs check_plain() for us, so we can
1242
      // use the "!" placeholder.
1243
      '!item' => $this->exportObjectVars($item),
1244
      '!entity' => $this->exportObjectVars($entity),
1245
    );
1246
1247
    return array($message, $arguments);
1248
  }
1249
1250
  /**
1251
   * Returns a string representation of an object or array for log messages.
1252
   *
1253
   * @param object|array $object
1254
   *   The object to convert.
1255
   *
1256
   * @return string
1257
   *   The sanitized string representation of the object.
1258
   */
1259
  protected function exportObjectVars($object) {
1260
    include_once DRUPAL_ROOT . '/includes/utility.inc';
1261
1262
    $out = is_array($object) ? $object : get_object_vars($object);
1263
    $out = array_filter($out, 'is_scalar');
1264
1265
    foreach ($out as $key => $value) {
1266
      if (is_string($value)) {
1267 7295e063 Assos Assos
        if (function_exists('mb_check_encoding') && !mb_check_encoding($value, 'UTF-8')) {
1268
          $value = utf8_encode($value);
1269
        }
1270 41cc1b08 Assos Assos
        $out[$key] = truncate_utf8($value, 100, FALSE, TRUE);
1271
      }
1272
    }
1273
1274
    if (is_array($object)) {
1275
      return check_plain(drupal_var_export($out));
1276
    }
1277
1278
    return check_plain(drupal_var_export((object) $out));
1279
  }
1280
1281
  /**
1282
   * Overrides FeedsPlugin::dependencies().
1283
   */
1284
  public function dependencies() {
1285
    $dependencies = parent::dependencies();
1286
1287
    // Find out which module defined the entity type.
1288
    $info = $this->entityInfo();
1289
    if (isset($info['module'])) {
1290
      $dependencies[$info['module']] = $info['module'];
1291
    }
1292
1293
    return $dependencies;
1294
  }
1295
1296 85ad3d82 Assos Assos
}
1297
1298
class FeedsProcessorBundleNotDefined extends Exception {}
1299 7295e063 Assos Assos
1300
/**
1301
 * Form after build callback for the field "update_existing".
1302
 *
1303
 * Adds descriptions to options of this field.
1304
 */
1305
function feeds_processor_config_form_update_existing_after_build($field) {
1306
  $field[FEEDS_REPLACE_EXISTING]['#description'] =  t('Loads records directly from the database, bypassing the Entity API. Faster, but use with caution.');
1307
  $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']);
1308
  return $field;
1309
}