Added missing modules, including some as submodules.
[yaffs-website] / web / modules / contrib / migrate_tools / src / MigrateExecutable.php
1 <?php
2
3 namespace Drupal\migrate_tools;
4
5 use Drupal\migrate\Event\MigratePreRowSaveEvent;
6 use Drupal\migrate\Event\MigrateRollbackEvent;
7 use Drupal\migrate\Event\MigrateRowDeleteEvent;
8 use Drupal\migrate\MigrateExecutable as MigrateExecutableBase;
9 use Drupal\migrate\MigrateMessageInterface;
10 use Drupal\migrate\Plugin\MigrationInterface;
11 use Drupal\migrate\MigrateSkipRowException;
12 use Drupal\migrate\Plugin\MigrateIdMapInterface;
13 use Drupal\migrate\Event\MigrateEvents;
14 use Drupal\migrate_plus\Event\MigrateEvents as MigratePlusEvents;
15 use Drupal\migrate\Event\MigrateMapSaveEvent;
16 use Drupal\migrate\Event\MigrateMapDeleteEvent;
17 use Drupal\migrate\Event\MigrateImportEvent;
18 use Drupal\migrate_plus\Event\MigratePrepareRowEvent;
19
20 class MigrateExecutable extends MigrateExecutableBase {
21
22   /**
23    * Counters of map statuses.
24    *
25    * @var array
26    *   Set of counters, keyed by MigrateIdMapInterface::STATUS_* constant.
27    */
28   protected $saveCounters = array(
29     MigrateIdMapInterface::STATUS_FAILED => 0,
30     MigrateIdMapInterface::STATUS_IGNORED => 0,
31     MigrateIdMapInterface::STATUS_IMPORTED => 0,
32     MigrateIdMapInterface::STATUS_NEEDS_UPDATE => 0,
33   );
34
35   /**
36    * Counter of map deletions.
37    *
38    * @var int
39    */
40   protected $deleteCounter = 0;
41
42   /**
43    * Maximum number of items to process in this migration. 0 indicates no limit
44    * is to be applied.
45    *
46    * @var int
47    */
48   protected $itemLimit = 0;
49
50   /**
51    * Frequency (in items) at which progress messages should be emitted.
52    *
53    * @var int
54    */
55   protected $feedback = 0;
56
57   /**
58    * List of specific source IDs to import.
59    *
60    * @var array
61    */
62   protected $idlist = [];
63
64   /**
65    * Count of number of items processed so far in this migration.
66    * @var int
67    */
68   protected $counter = 0;
69
70   /**
71    * Whether the destination item exists before saving.
72    *
73    * @var bool
74    */
75   protected $preExistingItem = FALSE;
76
77   /**
78    * List of event listeners we have registered.
79    *
80    * @var array
81    */
82   protected $listeners = [];
83
84   /**
85    * {@inheritdoc}
86    */
87   public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, array $options = []) {
88     parent::__construct($migration, $message);
89     if (isset($options['limit'])) {
90       $this->itemLimit = $options['limit'];
91     }
92     if (isset($options['feedback'])) {
93       $this->feedback = $options['feedback'];
94     }
95     if (isset($options['idlist'])) {
96       $this->idlist = explode(',', $options['idlist']);
97       array_walk($this->idlist , function(&$value, $key) {
98         $value = explode(':', $value);
99       });
100     }
101
102     $this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave'];
103     $this->listeners[MigrateEvents::MAP_DELETE] = [$this, 'onMapDelete'];
104     $this->listeners[MigrateEvents::POST_IMPORT] = [$this, 'onPostImport'];
105     $this->listeners[MigrateEvents::POST_ROLLBACK] = [$this, 'onPostRollback'];
106     $this->listeners[MigrateEvents::PRE_ROW_SAVE] = [$this, 'onPreRowSave'];
107     $this->listeners[MigrateEvents::POST_ROW_DELETE] = [$this, 'onPostRowDelete'];
108     $this->listeners[MigratePlusEvents::PREPARE_ROW] = [$this, 'onPrepareRow'];
109     foreach ($this->listeners as $event => $listener) {
110       \Drupal::service('event_dispatcher')->addListener($event, $listener);
111     }
112   }
113
114   /**
115    * Count up any map save events.
116    *
117    * @param \Drupal\migrate\Event\MigrateMapSaveEvent $event
118    *   The map event.
119    */
120   public function onMapSave(MigrateMapSaveEvent $event) {
121     // Only count saves for this migration.
122     if ($event->getMap()->getQualifiedMapTableName() == $this->migration->getIdMap()->getQualifiedMapTableName()) {
123       $fields = $event->getFields();
124       // Distinguish between creation and update.
125       if ($fields['source_row_status'] == MigrateIdMapInterface::STATUS_IMPORTED &&
126         $this->preExistingItem
127       ) {
128         $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE]++;
129       }
130       else {
131         $this->saveCounters[$fields['source_row_status']]++;
132       }
133     }
134   }
135
136   /**
137    * Count up any rollback events.
138    *
139    * @param \Drupal\migrate\Event\MigrateMapDeleteEvent $event
140    *   The map event.
141    */
142   public function onMapDelete(MigrateMapDeleteEvent $event) {
143     $this->deleteCounter++;
144   }
145
146   /**
147    * Return the number of items created.
148    *
149    * @return int
150    */
151   public function getCreatedCount() {
152     return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED];
153   }
154
155   /**
156    * Return the number of items updated.
157    *
158    * @return int
159    */
160   public function getUpdatedCount() {
161     return $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE];
162   }
163
164   /**
165    * Return the number of items ignored.
166    *
167    * @return int
168    */
169   public function getIgnoredCount() {
170     return $this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED];
171   }
172
173   /**
174    * Return the number of items that failed.
175    *
176    * @return int
177    */
178   public function getFailedCount() {
179     return $this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
180   }
181
182   /**
183    * Return the total number of items processed. Note that STATUS_NEEDS_UPDATE
184    * is not counted, since this is typically set on stubs created as side
185    * effects, not on the primary item being imported.
186    *
187    * @return int
188    */
189   public function getProcessedCount() {
190     return $this->saveCounters[MigrateIdMapInterface::STATUS_IMPORTED] +
191       $this->saveCounters[MigrateIdMapInterface::STATUS_NEEDS_UPDATE] +
192       $this->saveCounters[MigrateIdMapInterface::STATUS_IGNORED] +
193       $this->saveCounters[MigrateIdMapInterface::STATUS_FAILED];
194   }
195
196   /**
197    * Return the number of items rolled back.
198    *
199    * @return int
200    */
201   public function getRollbackCount() {
202     return $this->deleteCounter;
203   }
204
205   /**
206    * Reset all the per-status counters to 0.
207    */
208   protected function resetCounters() {
209     foreach ($this->saveCounters as $status => $count) {
210       $this->saveCounters[$status] = 0;
211     }
212     $this->deleteCounter = 0;
213   }
214
215   /**
216    * React to migration completion.
217    *
218    * @param \Drupal\migrate\Event\MigrateImportEvent $event
219    *   The map event.
220    */
221   public function onPostImport(MigrateImportEvent $event) {
222     $migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
223     $migrate_last_imported_store->set($event->getMigration()->id(), round(microtime(TRUE) * 1000));
224     $this->progressMessage();
225     $this->removeListeners();
226   }
227
228   /**
229    * Clean up all our event listeners.
230    */
231   protected function removeListeners() {
232     foreach ($this->listeners as $event => $listener) {
233       \Drupal::service('event_dispatcher')->removeListener($event, $listener);
234     }
235   }
236
237   /**
238    * Emit information on what we've done since the last feedback (or the
239    * beginning of this migration).
240    *
241    * @param bool $done
242    */
243   protected function progressMessage($done = TRUE) {
244     $processed = $this->getProcessedCount();
245     if ($done) {
246       $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
247       $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
248     }
249     else {
250       $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
251       $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
252     }
253     $this->message->display(\Drupal::translation()->formatPlural($processed,
254       $singular_message, $plural_message,
255         array('@numitems' => $processed,
256               '@created' => $this->getCreatedCount(),
257               '@updated' => $this->getUpdatedCount(),
258               '@failures' => $this->getFailedCount(),
259               '@ignored' => $this->getIgnoredCount(),
260               '@name' => $this->migration->id())));
261   }
262
263   /**
264    * React to rollback completion.
265    *
266    * @param \Drupal\migrate\Event\MigrateRollbackEvent $event
267    *   The map event.
268    */
269   public function onPostRollback(MigrateRollbackEvent $event) {
270     $this->rollbackMessage();
271     $this->removeListeners();
272   }
273
274   /**
275    * Emit information on what we've done since the last feedback (or the
276    * beginning of this migration).
277    *
278    * @param bool $done
279    */
280   protected function rollbackMessage($done = TRUE) {
281    $rolled_back = $this->getRollbackCount();
282    if ($done) {
283      $singular_message = "Rolled back 1 item - done with '@name'";
284      $plural_message = "Rolled back @numitems items - done with '@name'";
285    }
286    else {
287      $singular_message = "Rolled back 1 item - continuing with '@name'";
288      $plural_message = "Rolled back @numitems items - continuing with '@name'";
289    }
290    $this->message->display(\Drupal::translation()->formatPlural($rolled_back,
291      $singular_message, $plural_message,
292        array('@numitems' => $rolled_back,
293              '@name' => $this->migration->id())));
294  }
295
296   /**
297    * React to an item about to be imported.
298    *
299    * @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
300    *   The pre-save event.
301    */
302   public function onPreRowSave(MigratePreRowSaveEvent $event) {
303     $id_map = $event->getRow()->getIdMap();
304     if (!empty($id_map['destid1'])) {
305       $this->preExistingItem = TRUE;
306     }
307     else {
308       $this->preExistingItem = FALSE;
309     }
310   }
311
312   /**
313    * React to item rollback.
314    *
315    * @param \Drupal\migrate\Event\MigrateRowDeleteEvent $event
316    *   The post-save event.
317    */
318   public function onPostRowDelete(MigrateRowDeleteEvent $event) {
319     if ($this->feedback && ($this->deleteCounter) && $this->deleteCounter % $this->feedback == 0) {
320       $this->rollbackMessage(FALSE);
321       $this->resetCounters();
322     }
323   }
324
325   /**
326    * React to a new row.
327    *
328    * @param \Drupal\migrate_plus\Event\MigratePrepareRowEvent $event
329    *   The prepare-row event.
330    *
331    * @throws \Drupal\migrate\MigrateSkipRowException
332    *
333    */
334   public function onPrepareRow(MigratePrepareRowEvent $event) {
335     if (!empty($this->idlist)) {
336       $row = $event->getRow();
337       /**
338        * @TODO replace for $source_id = $row->getSourceIdValues(); when https://www.drupal.org/node/2698023 is fixed
339        */
340       $migration = $event->getMigration();
341       $source_id = array_merge(array_flip(array_keys($migration->getSourcePlugin()
342         ->getIds())), $row->getSourceIdValues());
343       $skip = TRUE;
344       foreach ($this->idlist as $item) {
345         if (array_values($source_id) === $item) {
346           $skip = FALSE;
347           break;
348         }
349       }
350       if ($skip) {
351         throw new MigrateSkipRowException(NULL, FALSE);
352       }
353     }
354     if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
355       $this->progressMessage(FALSE);
356       $this->resetCounters();
357     }
358     $this->counter++;
359     if ($this->itemLimit && ($this->getProcessedCount() + 1) >= $this->itemLimit) {
360       $event->getMigration()->interruptMigration(MigrationInterface::RESULT_COMPLETED);
361     }
362
363   }
364
365 }