migrate.drush.inc 47 KB

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