migrate.drush.inc 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384
  1. <?php
  2. /**
  3. * @file
  4. * Drush support for the migrate module
  5. */
  6. /**
  7. * Implements hook_drush_command().
  8. */
  9. function migrate_drush_command() {
  10. $migration_options = array(
  11. 'limit' => 'Limit on the length of each migration process, expressed in seconds or number of items',
  12. 'feedback' => 'Frequency of progress messages, in seconds or items processed',
  13. 'idlist' => 'A comma delimited list of ids to import or rollback. If unspecified, migrate imports all pending items or rolls back all items for the content set.',
  14. 'all' => 'Process all migrations that come after the specified migration. If no value is supplied, all migrations are processed.',
  15. 'instrument' => 'Capture performance information (timer, memory, or all)',
  16. 'force' => 'Force an operation to run, even if all dependencies are not satisfied',
  17. 'group' => 'Name of the migration group to run',
  18. );
  19. $items['migrate-status'] = array(
  20. 'description' => 'List all migrations with current status.',
  21. 'options' => array(
  22. 'refresh' => 'Recognize new migrations and update counts',
  23. 'group' => 'Name of the migration group to list',
  24. ),
  25. 'arguments' => array(
  26. 'migration' => 'Restrict to a single migration. Optional',
  27. ),
  28. 'examples' => array(
  29. 'migrate-status' => 'Retrieve status for all migrations',
  30. 'migrate-status BeerNode' => 'Retrieve status for just one migration',
  31. ),
  32. 'drupal dependencies' => array('migrate'),
  33. 'aliases' => array('ms'),
  34. );
  35. $items['migrate-fields-destination'] = array(
  36. 'description' => 'List the fields available for mapping in a destination.',
  37. 'options' => array(
  38. 'all' => $migration_options['all'],
  39. 'group' => $migration_options['group'],
  40. ),
  41. 'arguments' => array(
  42. 'migration' => 'Name of the migration or destination class to query for fields',
  43. ),
  44. 'examples' => array(
  45. 'migrate-fields-destination MyNode' => 'List fields for the destination in the MyNode migration',
  46. ),
  47. 'drupal dependencies' => array('migrate'),
  48. 'aliases' => array('mfd'),
  49. );
  50. $items['migrate-fields-source'] = array(
  51. 'description' => 'List the fields available for mapping from a source.',
  52. 'arguments' => array(
  53. 'migration' => 'Name of the migration or destination class to query for fields',
  54. ),
  55. 'options' => array(
  56. 'all' => $migration_options['all'],
  57. 'group' => $migration_options['group'],
  58. ),
  59. 'examples' => array(
  60. 'migrate-fields-destination MyNode' => 'List fields in the source query for the MyNode migration',
  61. ),
  62. 'drupal dependencies' => array('migrate'),
  63. 'aliases' => array('mfs'),
  64. );
  65. $items['migrate-mappings'] = array(
  66. 'description' => 'View information on all field mappings in a migration.',
  67. 'options' => array(
  68. 'all' => $migration_options['all'],
  69. 'group' => $migration_options['group'],
  70. 'csv' => 'Export information as a CSV',
  71. 'full' => 'Include more information on each mapping',
  72. ),
  73. 'arguments' => array(
  74. 'migration' => 'Name of the migration',
  75. ),
  76. 'examples' => array(
  77. 'migrate-mappings MyNode' => 'Show mappings for the MyNode migration',
  78. 'migrate-mappings MyNode --csv --full' => 'Export full mapping information in CSV format',
  79. ),
  80. 'drupal dependencies' => array('migrate'),
  81. 'aliases' => array('mm'),
  82. );
  83. $items['migrate-messages'] = array(
  84. 'description' => 'View any messages associated with a migration.',
  85. 'options' => array(
  86. 'csv' => 'Export messages as a CSV',
  87. ),
  88. 'arguments' => array(
  89. 'migration' => 'Name of the migration',
  90. ),
  91. 'examples' => array(
  92. 'migrate-messages MyNode' => 'Show all messages for the MyNode migration',
  93. ),
  94. 'drupal dependencies' => array('migrate'),
  95. 'aliases' => array('mmsg'),
  96. );
  97. $items['migrate-analyze'] = array(
  98. 'description' => 'Analyze the source fields for a migration.',
  99. 'options' => array(
  100. 'all' => $migration_options['all'],
  101. 'group' => $migration_options['group'],
  102. ),
  103. 'arguments' => array(
  104. 'migration' => 'Name of the migration',
  105. ),
  106. 'examples' => array(
  107. 'migrate-analyze MyNode' => 'Report on field values for the MyNode migration',
  108. ),
  109. 'drupal dependencies' => array('migrate'),
  110. 'aliases' => array('maz'),
  111. );
  112. $items['migrate-audit'] = array(
  113. 'description' => 'View information on problems in a migration.',
  114. 'options' => array(
  115. 'all' => $migration_options['all'],
  116. 'group' => $migration_options['group'],
  117. ),
  118. 'arguments' => array(
  119. 'migration' => 'Name of the migration',
  120. ),
  121. 'examples' => array(
  122. 'migrate-audit MyNode' => 'Report on problems in the MyNode migration',
  123. ),
  124. 'drupal dependencies' => array('migrate'),
  125. 'aliases' => array('ma'),
  126. );
  127. $items['migrate-rollback'] = array(
  128. 'description' => 'Roll back the destination objects from a given migration',
  129. 'options' => $migration_options,
  130. // We will bootstrap to login from within the command callback.
  131. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
  132. 'arguments' => array(
  133. 'migration' => 'Name of migration(s) to roll back. Delimit multiple using commas.',
  134. ),
  135. 'examples' => array(
  136. 'migrate-rollback Article' => 'Roll back the article migration',
  137. 'migrate-rollback Article --idlist=4,9' => 'Roll back two articles. The ids refer to the value of the primary key in base table',
  138. 'migrate-rollback User --limit="50 items"' =>
  139. 'Roll back up to 50 items from the migration named User',
  140. 'migrate-rollback User --feedback="60 seconds"' => 'Display a progress message every 60 seconds or less',
  141. ),
  142. 'drupal dependencies' => array('migrate'),
  143. 'aliases' => array('mr'),
  144. );
  145. $migration_options['update'] = 'In addition to processing unimported items from the source, update previously-imported items with new data';
  146. $migration_options['needs-update'] =
  147. 'Reimport up to 10K records where needs_update=1. This option is only needed when your Drupal DB is on a different DB server from your source data. Otherwise, these records get migrated with just migrate-import.';
  148. $migration_options['stop'] = 'Stop specified migration(s) if applicable.';
  149. $migration_options['rollback'] = 'Rollback specified migration(s) if applicable.';
  150. $migration_options['file_function'] = 'Override file function to use when migrating images.';
  151. $items['migrate-import'] = array(
  152. 'description' => 'Perform one or more migration processes',
  153. 'options' => $migration_options,
  154. 'arguments' => array(
  155. 'migration' => 'Name of migration(s) to import. Delimit multiple using commas.',
  156. ),
  157. 'examples' => array(
  158. 'migrate-import Article' => 'Import new articles',
  159. 'migrate-import Article --update' => 'Import new items, and also update previously-imported items',
  160. 'migrate-import Article --idlist=4,9' => 'Import two specific articles. The ids refer to the value of the primary key in base table',
  161. 'migrate-import Article --limit="60 seconds" --stop --rollback' =>
  162. 'Import for up to 60 seconds after stopping and rolling back the Article migration.',
  163. 'migrate-import Article --limit="100 items"' =>
  164. 'Import up to 100 items from the migration named Article.',
  165. 'migrate-import User --feedback="1000 items"' => 'Display a progress message every 1000 processed items or less',
  166. 'migrate-import --all=User' => 'Perform User migrations and all that follow it.',
  167. ),
  168. 'drupal dependencies' => array('migrate'),
  169. 'aliases' => array('mi'),
  170. );
  171. $items['migrate-stop'] = array(
  172. 'description' => 'Stop an active migration operation',
  173. 'options' => array('all' => 'Stop all active migration operations'),
  174. 'arguments' => array(
  175. 'migration' => 'Name of migration to stop',
  176. ),
  177. 'examples' => array(
  178. 'migrate-stop Article' => 'Stop any active operation on the Article migration',
  179. 'migrate-stop --all' => 'Stop all active migration operations',
  180. ),
  181. 'drupal dependencies' => array('migrate'),
  182. 'aliases' => array('mst'),
  183. );
  184. $items['migrate-reset-status'] = array(
  185. 'description' => 'Reset a active migration\'s status to idle',
  186. 'options' => array('all' => 'Reset all active migration operations'),
  187. 'arguments' => array(
  188. 'migration' => 'Name of migration to reset',
  189. ),
  190. 'examples' => array(
  191. 'migrate-reset-status Article' => 'Reset any active operation on the Article migration',
  192. 'migrate-reset-status --all' => 'Reset all active migration operations',
  193. ),
  194. 'drupal dependencies' => array('migrate'),
  195. 'aliases' => array('mrs'),
  196. );
  197. $items['migrate-deregister'] = array(
  198. 'description' => 'Remove all tracking of a migration',
  199. 'options' => array('orphans' => 'Remove tracking for any migrations whose implementing class no longer exists'),
  200. 'arguments' => array(
  201. 'migration' => 'Name of migration to deregister',
  202. ),
  203. 'examples' => array(
  204. 'migrate-deregister Article' => 'Deregister the Article migration',
  205. 'migrate-deregister --orphans' => 'Deregister any no-longer-implemented migrations',
  206. ),
  207. 'drupal dependencies' => array('migrate'),
  208. );
  209. $items['migrate-auto-register'] = array(
  210. 'description' => 'Register any newly-defined migration classes',
  211. 'drupal dependencies' => array('migrate'),
  212. 'aliases' => array('mar'),
  213. );
  214. $items['migrate-wipe'] = array(
  215. 'description' => 'Delete all nodes from specified content types.',
  216. 'examples' => array(
  217. "migrate-wipe story article" => 'Delete all story and article nodes.',
  218. ),
  219. 'arguments' => array(
  220. 'type' => 'A space delimited list of content type machine readable Ids.',
  221. ),
  222. 'drupal dependencies' => array('migrate'),
  223. 'aliases' => array('mw'),
  224. );
  225. return $items;
  226. }
  227. /**
  228. * Get the value of all migrate related options. Used when spawning a subshell.
  229. * Don't pass along all, stop, update, and rollback options.
  230. *
  231. * @return
  232. * An array of command specific options and their values.
  233. */
  234. function drush_migrate_get_options() {
  235. $options = array();
  236. $blacklist = array('stop', 'rollback', 'update', 'all');
  237. $command = drush_parse_command();
  238. foreach ($command['options'] as $key => $value) {
  239. // Strip leading --
  240. $key = ltrim($key, '-');
  241. if (!in_array($key, $blacklist)) {
  242. $value = drush_get_option($key);
  243. if (isset($value)) {
  244. $options[$key] = $value;
  245. }
  246. }
  247. }
  248. return $options;
  249. }
  250. /*
  251. * Spawn a subshell which runs the same command we are currently running.
  252. */
  253. function drush_migrate_invoke_process($migrations = '') {
  254. $args = drush_get_arguments();
  255. $options = drush_migrate_get_options();
  256. if (intval(DRUSH_MAJOR_VERSION) < 4) {
  257. // @todo: use drush_backend_invoke_args() as per http://drupal.org/node/658420.
  258. return drush_backend_invoke(implode(' ', $args), $options);
  259. }
  260. else {
  261. // $args[0] is the command name, $args[1] is the list of migrations.
  262. if (empty($migrations)) {
  263. $command_args = array($args[1]);
  264. }
  265. else {
  266. $command_args = array($migrations);
  267. }
  268. $return = drush_invoke_process('@self', $args[0], $command_args, $options);
  269. return $return;
  270. }
  271. }
  272. /**
  273. * A simplified version of the dashboard page.
  274. */
  275. function drush_migrate_status($name = NULL) {
  276. try {
  277. $refresh = drush_get_option('refresh');
  278. $group_option = drupal_strtolower(drush_get_option('group'));
  279. // Validate input and load Migration(s).
  280. if ($name) {
  281. if ($migration = MigrationBase::getInstance($name)) {
  282. $migrations = array($migration);
  283. }
  284. else {
  285. return drush_set_error(dt('Unrecognized migration: !cn', array('!cn' => $name)));
  286. }
  287. }
  288. else {
  289. $migrations = migrate_migrations();
  290. }
  291. $groups = MigrateGroup::groups();
  292. $table = array();
  293. foreach ($groups as $group) {
  294. if ($group_option && drupal_strtolower($group->getName()) != $group_option) {
  295. continue;
  296. }
  297. $group_members_count = 0;
  298. foreach ($migrations as $migration) {
  299. if ($migration->getGroup() != $group) {
  300. // This migration is not from this group.
  301. continue;
  302. }
  303. ++$group_members_count;
  304. if ($group_members_count == 1) {
  305. // An empty line and the headers.
  306. $table[] = array('');
  307. $table[] = array(dt('Group: !name', array('!name' => $group->getName())), dt('Total'), dt('Imported'), dt('Unimported'), dt('Status'), dt('Last imported'));
  308. }
  309. $has_counts = TRUE;
  310. if (method_exists($migration, 'sourceCount')) {
  311. $total = $migration->sourceCount($refresh);
  312. if ($total < 0) {
  313. $has_counts = FALSE;
  314. $total = dt('N/A');
  315. }
  316. }
  317. else {
  318. $has_counts = FALSE;
  319. $total = dt('N/A');
  320. }
  321. if (method_exists($migration, 'importedCount')) {
  322. $imported = $migration->importedCount();
  323. $processed = $migration->processedCount();
  324. }
  325. else {
  326. $has_counts = FALSE;
  327. $imported = dt('N/A');
  328. }
  329. if ($has_counts) {
  330. $unimported = $total - $processed;
  331. }
  332. else {
  333. $unimported = dt('N/A');
  334. }
  335. $status = $migration->getStatus();
  336. switch ($status) {
  337. case MigrationBase::STATUS_IDLE:
  338. $status = dt('Idle');
  339. break;
  340. case MigrationBase::STATUS_IMPORTING:
  341. $status = dt('Importing');
  342. break;
  343. case MigrationBase::STATUS_ROLLING_BACK:
  344. $status = dt('Rolling back');
  345. break;
  346. case MigrationBase::STATUS_STOPPING:
  347. $status = dt('Stopping');
  348. break;
  349. case MigrationBase::STATUS_DISABLED:
  350. $status = dt('Disabled');
  351. break;
  352. default:
  353. $status = dt('Unknown');
  354. break;
  355. }
  356. $table[] = array($migration->getMachineName(), $total, $imported, $unimported, $status, $migration->getLastImported());
  357. }
  358. }
  359. drush_print_table($table);
  360. }
  361. catch (MigrateException $e) {
  362. drush_print($e->getMessage());
  363. exit;
  364. }
  365. }
  366. // TODO: Use drush_choice for detailed field info
  367. function drush_migrate_fields_destination($args = NULL) {
  368. try {
  369. $migrations = drush_migrate_get_migrations($args);
  370. foreach ($migrations as $name => $migration) {
  371. drush_print("\n" . dt('@migration Destination Fields', array('@migration' => $name)) . "\n");
  372. $destination = $migration->getDestination();
  373. if (method_exists($destination, 'fields')) {
  374. $table = array();
  375. foreach ($destination->fields($migration) as $machine_name => $description) {
  376. $table[] = array($description, $machine_name);
  377. }
  378. drush_print_table($table);
  379. }
  380. else {
  381. drush_print(dt('No fields were found.'));
  382. }
  383. }
  384. }
  385. catch (MigrateException $e) {
  386. drush_print($e->getMessage());
  387. exit;
  388. }
  389. }
  390. function drush_migrate_fields_source($args = NULL) {
  391. try {
  392. $migrations = drush_migrate_get_migrations($args);
  393. foreach ($migrations as $name => $migration) {
  394. drush_print("\n" . dt('@migration Source Fields', array('@migration' => $name)) . "\n");
  395. $source = $migration->getSource();
  396. if (method_exists($source, 'fields')) {
  397. $table = array();
  398. foreach ($source->fields() as $machine_name => $description) {
  399. $table[] = array($description, $machine_name);
  400. }
  401. drush_print_table($table);
  402. }
  403. else {
  404. drush_print(dt('No fields were found.'));
  405. }
  406. }
  407. }
  408. catch (MigrateException $e) {
  409. drush_print($e->getMessage());
  410. exit;
  411. }
  412. }
  413. /**
  414. * Display field mappings for a migration.
  415. */
  416. function drush_migrate_mappings($args = NULL) {
  417. try {
  418. $full = drush_get_option('full');
  419. $migrations = drush_migrate_get_migrations($args);
  420. foreach ($migrations as $name => $migration) {
  421. drush_print("\n" . dt('@migration Mappings', array('@migration' => $name)) . "\n");
  422. // In verbose mode, we'll also get source and destination field descriptions
  423. if ($full) {
  424. $destination = $migration->getDestination();
  425. $dest_descriptions = array();
  426. if (method_exists($destination, 'fields')) {
  427. foreach ($destination->fields($migration) as $machine_name => $description) {
  428. $dest_descriptions[$machine_name] = $description;
  429. }
  430. }
  431. $source = $migration->getSource();
  432. $src_descriptions = array();
  433. if (method_exists($source, 'fields')) {
  434. foreach ($source->fields() as $machine_name => $description) {
  435. $src_descriptions[$machine_name] = $description;
  436. }
  437. }
  438. }
  439. if (method_exists($migration, 'getFieldMappings')) {
  440. // First group the mappings. We want "interesting" mappings first, so
  441. // put the boring Done and DNM mappings last.
  442. $descriptions = array();
  443. $done = array();
  444. $dnm = array();
  445. foreach ($migration->getFieldMappings() as $mapping) {
  446. $group = $mapping->getIssueGroup();
  447. $lowergroup = drupal_strtolower($group);
  448. if ($lowergroup == dt('done')) {
  449. $done[$group][] = $mapping;
  450. }
  451. elseif ($lowergroup == dt('dnm') || $lowergroup == dt('do not migrate')) {
  452. $dnm[$group][] = $mapping;
  453. }
  454. else {
  455. $descriptions[$group][] = $mapping;
  456. }
  457. }
  458. $descriptions = array_merge($descriptions, $done, $dnm);
  459. // Put out each group header
  460. $table = array();
  461. if ($full) {
  462. $table[] = array(dt('Destination'), dt(''), dt('Source'), dt(''), dt('Default'),
  463. dt('Description'));
  464. }
  465. else {
  466. $table[] = array(dt('Destination'), dt('Source'), dt('Default'),
  467. dt('Description'));
  468. }
  469. $first = TRUE;
  470. foreach ($descriptions as $group => $mappings) {
  471. if ($first) {
  472. $first = FALSE;
  473. }
  474. else {
  475. $table[] = array(' ');
  476. }
  477. // Attempt to highlight the group header a bit so it stands out
  478. $group_header = '--- ' . drupal_strtoupper($group) . ' ---';
  479. $table[] = array($group_header);
  480. foreach ($mappings as $mapping) {
  481. if (is_array($mapping->getDefaultValue())) {
  482. $default = implode(',', $mapping->getDefaultValue());
  483. }
  484. else {
  485. $default = $mapping->getDefaultValue();
  486. }
  487. $destination = $mapping->getDestinationField();
  488. $source = $mapping->getSourceField();
  489. if ($full) {
  490. if ($destination && $dest_descriptions[$destination]) {
  491. $dest_description = $dest_descriptions[$destination];
  492. }
  493. else {
  494. $dest_description = '';
  495. }
  496. if ($source && $src_descriptions[$source]) {
  497. $src_description = $src_descriptions[$source];
  498. }
  499. else {
  500. $src_description = '';
  501. }
  502. $table[] = array($destination, $dest_description, $source, $src_description,
  503. $default, $mapping->getDescription());
  504. }
  505. else {
  506. $table[] = array($destination, $source,
  507. $default, $mapping->getDescription());
  508. }
  509. }
  510. }
  511. if (drush_get_option('csv')) {
  512. foreach ($table as $row) {
  513. fputcsv(STDOUT, $row);
  514. }
  515. }
  516. else {
  517. drush_print_table($table, TRUE);
  518. }
  519. }
  520. }
  521. }
  522. catch (MigrateException $e) {
  523. drush_print($e->getMessage());
  524. exit;
  525. }
  526. }
  527. /**
  528. * Display messages for a migration.
  529. */
  530. function drush_migrate_messages($migration_name) {
  531. try {
  532. $migration = MigrationBase::getInstance($migration_name);
  533. if (is_a($migration, 'Migration')) {
  534. $map = $migration->getMap();
  535. $message_table = $map->getMessageTable();
  536. $result = db_select($message_table, 'msg', array('fetch' => PDO::FETCH_ASSOC))
  537. ->fields('msg')
  538. ->execute();
  539. $first = TRUE;
  540. $table = array();
  541. foreach ($result as $row) {
  542. unset($row['msgid']);
  543. unset($row['level']);
  544. if ($first) {
  545. $table[] = array_keys($row);
  546. $first = FALSE;
  547. }
  548. $table[] = $row;
  549. }
  550. }
  551. if (empty($table)) {
  552. drush_log(dt('No messages for this migration'), 'status');
  553. }
  554. else {
  555. if (drush_get_option('csv')) {
  556. foreach ($table as $row) {
  557. fputcsv(STDOUT, $row);
  558. }
  559. }
  560. else {
  561. $widths = array();
  562. foreach ($table[0] as $header) {
  563. $widths[] = strlen($header) + 1;
  564. }
  565. drush_print_table($table, TRUE, $widths);
  566. }
  567. }
  568. }
  569. catch (MigrateException $e) {
  570. drush_print($e->getMessage());
  571. exit;
  572. }
  573. }
  574. /**
  575. * Analyze the source fields for any passed migrations.
  576. */
  577. function drush_migrate_analyze($args = NULL) {
  578. $migrations = drush_migrate_get_migrations($args);
  579. foreach ($migrations as $name => $migration) {
  580. // "Migrations" derived from MigrationBase won't have an analyze method.
  581. if (method_exists($migration, 'analyze')) {
  582. drush_print("\n" . dt('Analyzing @migration', array('@migration' => $name)) . "\n");
  583. $analysis = $migration->analyze();
  584. if (!empty($analysis)) {
  585. foreach ($analysis as $field_name => $details) {
  586. if (!empty($details['description'])) {
  587. drush_print(dt('@name (@description):', array('@name' => $field_name,
  588. '@description' => $details['description'])));
  589. }
  590. else {
  591. drush_print(dt('@name:', array('@name' => $field_name)));
  592. }
  593. // Special handling in degenerate cases
  594. if (count($details['distinct_values']) == 1) {
  595. $value = trim(reset(array_keys($details['distinct_values'])));
  596. if ($value === '') {
  597. drush_print(' ' . dt('The field is always empty'));
  598. }
  599. else {
  600. drush_print(' ' . dt('Only one value present: @value',
  601. array('@value' => $value)));
  602. }
  603. }
  604. else {
  605. if ($details['is_numeric']) {
  606. drush_print(' ' . dt('Numeric field with a range of @min to @max',
  607. array('@min' => $details['min_numeric'], '@max' => $details['max_numeric'])));
  608. }
  609. else {
  610. drush_print(' ' . dt('String field with a length ranging from @min to @max',
  611. array('@min' => $details['min_strlen'], '@max' => $details['max_strlen'])));
  612. }
  613. $values = array();
  614. $header = NULL;
  615. // If the max of 10 tracked distinct values was reached, we assume
  616. // there are many values and treat them as samples. Under 10 this
  617. // may be an enumerated field, show all values with their counts.
  618. if (count($details['distinct_values']) < 10) {
  619. drush_print(' ' . dt('Distinct values:'));
  620. $header = array('', dt('Value'), dt('Count'));
  621. $values[] = $header;
  622. }
  623. else {
  624. drush_print(' ' . dt('Sample values:'));
  625. }
  626. ksort($details['distinct_values']);
  627. foreach ($details['distinct_values'] as $value => $count) {
  628. // Truncate long strings
  629. $value = substr($value, 0, 60);
  630. if (strlen($value) == 60) {
  631. $value .= dt('...');
  632. }
  633. $row = array(' ', $value);
  634. if (count($details['distinct_values']) < 10) {
  635. $row[] = $count;
  636. }
  637. $values[] = $row;
  638. }
  639. // No header for sample values.
  640. drush_print_table($values, !is_null($header));
  641. }
  642. }
  643. }
  644. }
  645. }
  646. }
  647. /**
  648. * Display field mappings for a migration.
  649. */
  650. function drush_migrate_audit($args = NULL) {
  651. try {
  652. $problem_descriptions = array(
  653. 'wtf' => dt("Probably an incomplete migration:"),
  654. 'noted_issues' => dt("Noted as an issue:"),
  655. // I wish drush had dformat_plural().
  656. 'sources_unmapped' => dt("Source(s) not used in a mapping:"),
  657. 'sources_missing' => dt("Used as source field in mapping but not in source field list:"),
  658. 'destinations_unmapped' => dt("Destination(s) not used in a mapping:"),
  659. 'destinations_missing' => dt("Used as destination field in mapping but not in destination field list:"),
  660. );
  661. drush_print("Auditing migrations");
  662. $migrations = drush_migrate_get_migrations($args);
  663. foreach ($migrations as $name => $migration) {
  664. $problems = array();
  665. foreach ($problem_descriptions as $key => $description) {
  666. $problems[$key] = array();
  667. }
  668. drush_print("\n" . dt('@migration', array('@migration' => $name)) . "\n");
  669. if (!method_exists($migration, 'getSource') || !($source = $migration->getSource())) {
  670. $problems['wtf'][] = dt('Missing a source');
  671. $source_fields = array();
  672. }
  673. else {
  674. $source_fields = $source->fields();
  675. }
  676. if (!method_exists($migration, 'getDestination') || !($destination = $migration->getDestination())) {
  677. $problems['wtf'][] = dt('Missing a destination');
  678. $destination_fields = array();
  679. }
  680. else {
  681. $destination_fields = $destination->fields($migration);
  682. }
  683. if (!method_exists($migration, 'getFieldMappings')) {
  684. $problems['wtf'][] = dt('Missing field mappings');
  685. $field_mappings = array();
  686. }
  687. else {
  688. $field_mappings = $migration->getFieldMappings();
  689. }
  690. $used_sources = array();
  691. $used_destinations = array();
  692. foreach ($field_mappings as $mapping) {
  693. $source_field = $mapping->getSourceField();
  694. $destination_field = $mapping->getDestinationField();
  695. $used_sources[$source_field] = TRUE;
  696. $used_destinations[$destination_field] = TRUE;
  697. $issue_priority = $mapping->getIssuePriority();
  698. if (!is_null($issue_priority) && $issue_priority != MigrateFieldMapping::ISSUE_PRIORITY_OK) {
  699. $problems['noted_issues'][] = array(
  700. dt('Source') => $source_field,
  701. dt('Destination') => $destination_field,
  702. dt('Priority') => MigrateFieldMapping::$priorities[$issue_priority],
  703. dt('Description') => $mapping->getDescription(),
  704. );
  705. }
  706. // Validate source and destination fields actually exist
  707. if (!is_null($source_field) && !isset($source_fields[$source_field])) {
  708. $problems['sources_missing'][] = $source_field;
  709. }
  710. if (!is_null($destination_field) && !isset($destination_fields[$destination_field])) {
  711. $problems['destinations_missing'][] = $destination_field;
  712. }
  713. }
  714. foreach (array_diff_key($source_fields, $used_sources) as $name => $description) {
  715. $problems['sources_unmapped'][] = array('Field' => $name, 'Description' => $description);
  716. }
  717. foreach (array_diff_key($destination_fields, $used_destinations) as $name => $description) {
  718. $problems['destinations_unmapped'][] = array('Field' => $name, 'Description' => $description);
  719. }
  720. $problems = array_filter($problems);
  721. if (empty($problems)) {
  722. drush_print(dt('No problems found.') . "\n", 1);
  723. }
  724. else {
  725. foreach ($problems as $type => $some_problems) {
  726. drush_print($problem_descriptions[$type]);
  727. // If the contents of each row are arrays print it as a table.
  728. if (is_array($some_problems[0])) {
  729. $table = array_merge(array(array_keys($some_problems[0])), $some_problems);
  730. drush_print_table($table, TRUE);
  731. }
  732. else {
  733. foreach ($some_problems as $problem) {
  734. drush_print($problem, 1);
  735. }
  736. // Add an extra new line to keep the spacing consistent with the
  737. // tables.
  738. drush_print();
  739. }
  740. }
  741. }
  742. }
  743. }
  744. catch (MigrateException $e) {
  745. drush_print($e->getMessage());
  746. exit;
  747. }
  748. }
  749. /**
  750. * Roll back one specified migration
  751. */
  752. function drush_migrate_rollback($args = NULL) {
  753. try {
  754. $migrations = drush_migrate_get_migrations($args);
  755. // Rollback in reverse order
  756. $migrations = array_reverse($migrations, TRUE);
  757. $options = array();
  758. if ($idlist = drush_get_option('idlist', FALSE)) {
  759. $options['idlist'] = $idlist;
  760. }
  761. if (drush_get_option('force', FALSE) == 1) {
  762. $options['force'] = TRUE;
  763. }
  764. $limit = drush_get_option('limit');
  765. if ($limit) {
  766. $parts = explode(' ', $limit);
  767. $options['limit']['value'] = $parts[0];
  768. $options['limit']['unit'] = $parts[1];
  769. if (!$options['limit']['unit']) {
  770. $options['limit']['unit'] = 'items';
  771. }
  772. elseif ($options['limit']['unit'] != 'seconds' &&
  773. $options['limit']['unit'] != 'second' &&
  774. $options['limit']['unit'] != 'items' &&
  775. $options['limit']['unit'] != 'item') {
  776. drush_set_error(NULL, dt("Invalid limit unit '!unit'",
  777. array('!unit' => $options['limit']['unit'])));
  778. return;
  779. }
  780. }
  781. $feedback = drush_get_option('feedback');
  782. if ($feedback) {
  783. $parts = explode(' ', $feedback);
  784. $options['feedback']['value'] = $parts[0];
  785. $options['feedback']['unit'] = $parts[1];
  786. if ($options['feedback']['unit'] != 'seconds' &&
  787. $options['feedback']['unit'] != 'second' &&
  788. $options['feedback']['unit'] != 'items' &&
  789. $options['feedback']['unit'] != 'item') {
  790. drush_set_error(NULL, dt("Invalid feedback frequency unit '!unit'",
  791. array('!unit' => $options['feedback']['unit'])));
  792. return;
  793. }
  794. }
  795. $instrument = drush_get_option('instrument');
  796. global $_migrate_track_memory, $_migrate_track_timer;
  797. switch ($instrument) {
  798. case 'timer':
  799. $_migrate_track_timer = TRUE;
  800. break;
  801. case 'memory':
  802. $_migrate_track_memory = TRUE;
  803. break;
  804. case 'all':
  805. $_migrate_track_timer = TRUE;
  806. $_migrate_track_memory = TRUE;
  807. break;
  808. }
  809. foreach ($migrations as $migration) {
  810. drush_log(dt("Rolling back '!description' migration",
  811. array('!description' => $migration->getMachineName())));
  812. $return = $migration->processRollback($options);
  813. // If it couldn't finish (presumably because it was appraoching memory_limit),
  814. // continue in a subprocess
  815. if ($return == MigrationBase::RESULT_INCOMPLETE) {
  816. drush_migrate_invoke_process();
  817. }
  818. // If stopped, don't process any further
  819. elseif ($return == MigrationBase::RESULT_STOPPED) {
  820. break;
  821. }
  822. elseif ($return == MigrationBase::RESULT_SKIPPED) {
  823. drush_log(dt("Skipping migration !name due to unfulfilled dependencies, use the --force option to run it anyway.",
  824. array('!name' => $migration->getMachineName())),
  825. 'warning');
  826. }
  827. }
  828. }
  829. catch (MigrateException $e) {
  830. drush_print($e->getMessage());
  831. exit;
  832. }
  833. if ($_migrate_track_memory) {
  834. drush_migrate_print_memory();
  835. }
  836. if ($_migrate_track_timer && !drush_get_context('DRUSH_DEBUG')) {
  837. drush_print_timers();
  838. }
  839. }
  840. function drush_migrate_get_migrations($args) {
  841. $migration_objects = migrate_migrations();
  842. if ($start = drush_get_option('all')) {
  843. // Handle custom first migration when --all=foo is supplied.
  844. $seen = $start === TRUE ? TRUE : FALSE;
  845. foreach ($migration_objects as $name => $migration) {
  846. if (!$seen && (drupal_strtolower($start) . 'migration' == drupal_strtolower($name))) {
  847. // We found our starting migration. $seen is always TRUE now.
  848. $seen = TRUE;
  849. }
  850. if (!$migration->getEnabled() || !$seen) {
  851. // This migration is disabled or is before our starting migration.
  852. unset($migration_objects[$name]);
  853. }
  854. }
  855. }
  856. elseif ($group = drush_get_option('group')) {
  857. foreach ($migration_objects as $name => $migration) {
  858. if (drupal_strtolower($group) !=
  859. drupal_strtolower($migration->getGroup()->getName()) ||
  860. !$migration->getEnabled()) {
  861. unset($migration_objects[$name]);
  862. }
  863. }
  864. }
  865. else {
  866. $named_migrations = array();
  867. foreach (explode(',', $args) as $name) {
  868. $found = FALSE;
  869. foreach ($migration_objects as $machine_name => $migration) {
  870. if (drupal_strtolower($name) == drupal_strtolower($machine_name)) {
  871. if ($migration->getEnabled()) {
  872. $named_migrations[$name] = $migration;
  873. $found = TRUE;
  874. break;
  875. }
  876. else {
  877. drush_log(dt('Migration !name is disabled', array('!name' => $name)), 'warning');
  878. }
  879. }
  880. }
  881. if (!$found) {
  882. drush_log(dt('No migration with machine name !name found', array('!name' => $name)), 'error');
  883. }
  884. }
  885. $migration_objects = $named_migrations;
  886. }
  887. return $migration_objects;
  888. }
  889. // Implement drush_hook_COMMAND_validate().
  890. function drush_migrate_fields_destination_validate($args = NULL) {
  891. return drush_migrate_validate_common($args);
  892. }
  893. // Implement drush_hook_COMMAND_validate().
  894. function drush_migrate_fields_source_validate($args = NULL) {
  895. return drush_migrate_validate_common($args);
  896. }
  897. // Implement drush_hook_COMMAND_validate().
  898. function drush_migrate_mappings_validate($args = NULL) {
  899. return drush_migrate_validate_common($args);
  900. }
  901. // Implement drush_hook_COMMAND_validate().
  902. function drush_migrate_analyze_validate($args = NULL) {
  903. return drush_migrate_validate_common($args);
  904. }
  905. // Implement drush_hook_COMMAND_validate().
  906. function drush_migrate_audit_validate($args = NULL) {
  907. return drush_migrate_validate_common($args);
  908. }
  909. // Implement drush_hook_COMMAND_validate().
  910. function drush_migrate_import_validate($args = NULL) {
  911. return drush_migrate_validate_common($args);
  912. }
  913. // Implement drush_hook_COMMAND_validate().
  914. function drush_migrate_stop_validate($args = NULL) {
  915. return drush_migrate_validate_common($args);
  916. }
  917. // Implement drush_hook_COMMAND_validate().
  918. function drush_migrate_reset_status_validate($args = NULL) {
  919. return drush_migrate_validate_common($args);
  920. }
  921. // Implement drush_hook_COMMAND_validate().
  922. function drush_migrate_rollback_validate($args = NULL) {
  923. return drush_migrate_validate_common($args);
  924. }
  925. function drush_migrate_validate_common($args) {
  926. if (drush_get_option('all')) {
  927. if (!empty($args) || drush_get_option('group')) {
  928. return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
  929. }
  930. }
  931. elseif (drush_get_option('group')) {
  932. if (!empty($args) || drush_get_option('all')) {
  933. return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
  934. }
  935. }
  936. else {
  937. if (empty($args)) {
  938. return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
  939. }
  940. $machine_names = explode(',', $args);
  941. foreach ($machine_names as $machine_name) {
  942. $machine_name = trim($machine_name);
  943. $class_name = db_select('migrate_status', 'ms')
  944. ->fields('ms', array('class_name'))
  945. ->condition('machine_name', $machine_name)
  946. ->execute()
  947. ->fetchField();
  948. if (!$class_name || !class_exists($class_name)) {
  949. drush_set_error(dt('Unrecognized migration: !name', array('!name' => $machine_name)));
  950. }
  951. }
  952. }
  953. $feedback = drush_get_option('feedback');
  954. if ($feedback) {
  955. $parts = explode(' ', $feedback);
  956. $options['feedback']['value'] = $parts[0];
  957. $options['feedback']['unit'] = $parts[1];
  958. if ($options['feedback']['unit'] != 'seconds' &&
  959. $options['feedback']['unit'] != 'second' &&
  960. $options['feedback']['unit'] != 'items' &&
  961. $options['feedback']['unit'] != 'item') {
  962. drush_set_error(NULL, dt("Invalid feedback frequency unit '!unit'",
  963. array('!unit' => $options['feedback']['unit'])));
  964. return;
  965. }
  966. }
  967. }
  968. /*
  969. * A 'pre' callback for migrate-import command.
  970. * Call migrate-stop and migrate-rollback commands if requested.
  971. */
  972. function drush_migrate_pre_migrate_import($args = NULL) {
  973. if (drush_get_option('stop')) {
  974. drush_invoke('migrate-stop', $args);
  975. }
  976. if (drush_get_option('rollback')) {
  977. drush_unset_option('rollback');
  978. drush_invoke('migrate-rollback', $args);
  979. }
  980. }
  981. /**
  982. * Perform import on one or more migrations.
  983. *
  984. * @param $machine_names
  985. * A comma delimited list of machine names, or the special name 'all'
  986. */
  987. function drush_migrate_import($args = NULL) {
  988. try {
  989. $migrations = drush_migrate_get_migrations($args);
  990. $options = array();
  991. if ($idlist = drush_get_option('idlist', FALSE)) {
  992. $options['idlist'] = $idlist;
  993. }
  994. if ($file_function = drush_get_option('file_function', '')) {
  995. $options['file_function'] = $file_function;
  996. }
  997. if (drush_get_option('force', FALSE) == 1) {
  998. $options['force'] = TRUE;
  999. }
  1000. $limit = drush_get_option('limit');
  1001. if ($limit) {
  1002. $parts = explode(' ', $limit);
  1003. $options['limit']['value'] = $parts[0];
  1004. $options['limit']['unit'] = $parts[1];
  1005. if (!$options['limit']['unit']) {
  1006. $options['limit']['unit'] = 'items';
  1007. }
  1008. elseif ($options['limit']['unit'] != 'seconds' &&
  1009. $options['limit']['unit'] != 'second' &&
  1010. $options['limit']['unit'] != 'items' &&
  1011. $options['limit']['unit'] != 'item') {
  1012. drush_set_error(NULL, dt("Invalid limit unit '!unit'",
  1013. array('!unit' => $options['limit']['unit'])));
  1014. return;
  1015. }
  1016. }
  1017. $feedback = drush_get_option('feedback');
  1018. if ($feedback) {
  1019. $parts = explode(' ', $feedback);
  1020. $options['feedback']['value'] = $parts[0];
  1021. $options['feedback']['unit'] = $parts[1];
  1022. if ($options['feedback']['unit'] != 'seconds' &&
  1023. $options['feedback']['unit'] != 'second' &&
  1024. $options['feedback']['unit'] != 'items' &&
  1025. $options['feedback']['unit'] != 'item') {
  1026. drush_set_error(NULL, dt("Invalid feedback frequency unit '!unit'",
  1027. array('!unit' => $options['feedback']['unit'])));
  1028. return;
  1029. }
  1030. }
  1031. $instrument = drush_get_option('instrument');
  1032. global $_migrate_track_memory, $_migrate_track_timer;
  1033. switch ($instrument) {
  1034. case 'timer':
  1035. $_migrate_track_timer = TRUE;
  1036. break;
  1037. case 'memory':
  1038. $_migrate_track_memory = TRUE;
  1039. break;
  1040. case 'all':
  1041. $_migrate_track_timer = TRUE;
  1042. $_migrate_track_memory = TRUE;
  1043. break;
  1044. }
  1045. $stop = FALSE;
  1046. foreach ($migrations as $machine_name => $migration) {
  1047. drush_log(dt("Importing '!description' migration",
  1048. array('!description' => $machine_name)));
  1049. if (drush_get_option('update')) {
  1050. $migration->prepareUpdate();
  1051. }
  1052. if (drush_get_option('needs-update')) {
  1053. $map_rows = $migration->getMap()->getRowsNeedingUpdate(10000);
  1054. $idlist = array();
  1055. foreach ($map_rows as $row) {
  1056. $idlist[] = $row->sourceid1;
  1057. }
  1058. $options['idlist'] = implode(',', $idlist);
  1059. }
  1060. // The goal here is to do one migration in the parent process and then
  1061. // spawn subshells as needed when memory is depleted. We show feedback
  1062. // after each subshell depletes itself. Best we can do in PHP.
  1063. if (!drush_get_context('DRUSH_BACKEND')) {
  1064. // Our first pass and in the parent process. Run a migration right here.
  1065. $status = $migration->processImport($options);
  1066. if ($status == MigrationBase::RESULT_SKIPPED) {
  1067. drush_log(dt("Skipping migration !name due to unfulfilled dependencies:\n !depends\nUse the --force option to run it anyway.",
  1068. array(
  1069. '!name' => $machine_name,
  1070. '!depends' => implode("\n ", $migration->incompleteDependencies()),
  1071. )),
  1072. 'warning');
  1073. }
  1074. elseif ($status == MigrationBase::RESULT_STOPPED) {
  1075. break;
  1076. }
  1077. elseif ($status == MigrationBase::RESULT_INCOMPLETE) {
  1078. $stop = TRUE;
  1079. }
  1080. // Subsequent run in the parent process. Spawn subshells ad infinitum.
  1081. $migration_string = implode(',', array_keys($migrations));
  1082. while ($status == MigrationBase::RESULT_INCOMPLETE) {
  1083. $return = drush_migrate_invoke_process($migration_string);
  1084. // 'object' holds the return code we care about.
  1085. $status = $return['object']['status'];
  1086. $migration_string = $return['object']['migrations'];
  1087. if ($status == MigrationBase::RESULT_SKIPPED) {
  1088. drush_log(dt("Skipping migration !name due to unfulfilled dependencies:\n !depends\nUse the --force option to run it anyway.",
  1089. array(
  1090. '!name' => $machine_name,
  1091. '!depends' => implode("\n ", $migration->incompleteDependencies()),
  1092. )),
  1093. 'warning');
  1094. }
  1095. elseif ($status == MigrationBase::RESULT_STOPPED) {
  1096. $stop = TRUE;
  1097. break;
  1098. }
  1099. }
  1100. }
  1101. else {
  1102. // I'm in a subshell. Import then set return value so parent process can respawn or move on.
  1103. $status = $migration->processImport($options);
  1104. if ($status == MigrationBase::RESULT_SKIPPED) {
  1105. drush_log(dt("Skipping migration !name due to unfulfilled dependencies:\n !depends\n",
  1106. array(
  1107. '!name' => $machine_name,
  1108. '!depends' => implode("\n ", $migration->incompleteDependencies()),
  1109. )),
  1110. 'warning');
  1111. }
  1112. elseif ($status == MigrationBase::RESULT_INCOMPLETE) {
  1113. $stop = TRUE;
  1114. }
  1115. drush_backend_set_result(array('status' => $status,
  1116. 'migrations' => implode(',', array_keys($migrations))));
  1117. }
  1118. if ($stop) {
  1119. break;
  1120. }
  1121. unset($migrations[$machine_name]);
  1122. }
  1123. }
  1124. catch (MigrateException $e) {
  1125. drush_print($e->getMessage());
  1126. exit;
  1127. }
  1128. if ($_migrate_track_memory) {
  1129. drush_migrate_print_memory();
  1130. }
  1131. if ($_migrate_track_timer && !drush_get_context('DRUSH_DEBUG')) {
  1132. drush_print_timers();
  1133. }
  1134. }
  1135. //**
  1136. // * Stop clearing or importing a given content set.
  1137. // *
  1138. // * @param $content_set
  1139. // * The name of the Migration
  1140. // */
  1141. function drush_migrate_stop($args = NULL) {
  1142. try {
  1143. $migrations = drush_migrate_get_migrations($args);
  1144. foreach ($migrations as $migration) {
  1145. drush_log(dt("Stopping '!description' migration", array('!description' => $migration->getMachineName())));
  1146. $migration->stopProcess();
  1147. }
  1148. }
  1149. catch (MigrateException $e) {
  1150. drush_print($e->getMessage());
  1151. exit;
  1152. }
  1153. }
  1154. /**
  1155. * Reset the status of a given migration.
  1156. */
  1157. function drush_migrate_reset_status($args = NULL) {
  1158. try {
  1159. $migrations = drush_migrate_get_migrations($args);
  1160. foreach ($migrations as $migration) {
  1161. drush_log(dt("Resetting '!description' migration",
  1162. array('!description' => $migration->getMachineName())));
  1163. $migration->resetStatus();
  1164. }
  1165. }
  1166. catch (MigrateException $e) {
  1167. drush_print($e->getMessage());
  1168. exit;
  1169. }
  1170. }
  1171. /**
  1172. * Deregister a given migration, or all orphaned migrations. Note that the
  1173. * migration might no longer "exist" (the class implementation might be gone),
  1174. * so we can't count on being able to instantiate it, or use migrate_migrations().
  1175. */
  1176. function drush_migrate_deregister($args = NULL) {
  1177. try {
  1178. $orphans = drush_get_option('orphans');
  1179. if ($orphans) {
  1180. $migrations = array();
  1181. $result = db_select('migrate_status', 'ms')
  1182. ->fields('ms', array('class_name', 'machine_name'))
  1183. ->execute();
  1184. foreach ($result as $row) {
  1185. if (!class_exists($row->class_name)) {
  1186. $migrations[] = $row->machine_name;
  1187. }
  1188. }
  1189. }
  1190. else {
  1191. $migrations = explode(',', $args);
  1192. }
  1193. foreach ($migrations as $machine_name) {
  1194. drush_migrate_deregister_migration($machine_name);
  1195. drush_log(dt("Deregistered '!description' migration",
  1196. array('!description' => $machine_name)), 'success');
  1197. }
  1198. }
  1199. catch (MigrateException $e) {
  1200. drush_print($e->getMessage());
  1201. exit;
  1202. }
  1203. }
  1204. /**
  1205. * Given a migration machine name, remove its tracking from the database.
  1206. *
  1207. * @param $machine_name
  1208. */
  1209. function drush_migrate_deregister_migration($machine_name) {
  1210. // The class is gone, so we'll manually clear migrate_status, and make
  1211. // the default assumptions about the map/message tables.
  1212. db_drop_table('migrate_map_' . $machine_name);
  1213. db_drop_table('migrate_message_' . $machine_name);
  1214. db_delete('migrate_status')
  1215. ->condition('machine_name', $machine_name)
  1216. ->execute();
  1217. }
  1218. /**
  1219. * Register any previously-unrecognized non-dynamic migrations.
  1220. */
  1221. function drush_migrate_auto_register($args = NULL) {
  1222. migrate_autoregister();
  1223. }
  1224. /**
  1225. * A drush command callback.
  1226. */
  1227. function drush_migrate_wipe() {
  1228. $types = func_get_args();
  1229. $nids = db_select('node', 'n')
  1230. ->fields('n', array('nid'))
  1231. ->condition('type', $types, 'IN')
  1232. ->execute()
  1233. ->fetchCol();
  1234. $chunks = array_chunk($nids, 50);
  1235. foreach ($chunks as $chunk) {
  1236. node_delete_multiple($chunk);
  1237. }
  1238. migrate_instrument_stop('node_delete');
  1239. }
  1240. // Print all timers for the request.
  1241. function drush_migrate_print_memory() {
  1242. global $_migrate_memory;
  1243. $temparray = array();
  1244. foreach ((array)$_migrate_memory as $name => $memoryrec) {
  1245. // We have to use timer_read() for active timers, and check the record for others
  1246. if (isset($memoryrec['start'])) {
  1247. $temparray[$name] = migrate_memory_read($name);
  1248. }
  1249. else {
  1250. $temparray[$name] = $memoryrec['bytes'];
  1251. }
  1252. }
  1253. // Go no farther if there were no timers
  1254. if (count($temparray) > 0) {
  1255. // Put the highest cumulative times first
  1256. arsort($temparray);
  1257. $table = array();
  1258. $table[] = array('Name', 'Cum (bytes)', 'Count', 'Avg (bytes)');
  1259. foreach ($temparray as $name => $memory) {
  1260. $count = $_migrate_memory[$name]['count'];
  1261. if ($count > 0) {
  1262. $avg = round($memory/$count, 0);
  1263. }
  1264. else {
  1265. $avg = 'N/A';
  1266. }
  1267. $table[] = array($name, $memory, $count, $avg);
  1268. }
  1269. drush_print_table($table, TRUE);
  1270. }
  1271. }
  1272. /**
  1273. * Command argument complete callback.
  1274. *
  1275. * @return
  1276. * List of migrations.
  1277. */
  1278. function migrate_migrate_status_complete() {
  1279. return array('values' => drush_migrate_migrations());
  1280. }
  1281. function migrate_migrate_import_complete() {
  1282. return array('values' => drush_migrate_migrations());
  1283. }
  1284. function migrate_migrate_rollback_complete() {
  1285. return array('values' => drush_migrate_migrations());
  1286. }
  1287. function migrate_migrate_fields_destination_complete() {
  1288. return array('values' => drush_migrate_migrations());
  1289. }
  1290. function migrate_migrate_fields_source_complete() {
  1291. return array('values' => drush_migrate_migrations());
  1292. }
  1293. function migrate_migrate_mappings_complete() {
  1294. return array('values' => drush_migrate_migrations());
  1295. }
  1296. function migrate_migrate_reset_status_complete() {
  1297. return array('values' => drush_migrate_migrations());
  1298. }
  1299. function migrate_migrate_stop_complete() {
  1300. return array('values' => drush_migrate_migrations());
  1301. }
  1302. function drush_migrate_migrations() {
  1303. drush_bootstrap(DRUPAL_BOOTSTRAP_FULL);
  1304. $migrations = migrate_migrations();
  1305. foreach ($migrations as $migration) {
  1306. $values[] = $migration->getMachineName();
  1307. }
  1308. return $values;
  1309. }