bundle_copy.module 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. <?php
  2. /**
  3. * @file
  4. * Bundle copy.
  5. */
  6. /**
  7. * Api function to get the bundle copy info.
  8. */
  9. function bundle_copy_get_info() {
  10. static $info = FALSE;
  11. if (!$info) {
  12. return module_invoke_all('bundle_copy_info');
  13. }
  14. return $info;
  15. }
  16. /**
  17. * Implements hook_bundle_copy_info().
  18. */
  19. function bundle_copy_bundle_copy_info() {
  20. $info = array();
  21. $info['node'] = array(
  22. 'bundle_export_callback' => 'node_type_get_type',
  23. 'bundle_save_callback' => 'node_type_save',
  24. 'export_menu' => array(
  25. 'path' => 'admin/structure/types/export',
  26. 'access arguments' => 'administer content types',
  27. ),
  28. 'import_menu' => array(
  29. 'path' => 'admin/structure/types/import',
  30. 'access arguments' => 'administer content types',
  31. ),
  32. );
  33. $info['user'] = array(
  34. 'bundle_export_callback' => '_bc_bundle_export_ignore',
  35. 'bundle_save_callback' => '_bc_bundle_save_ignore',
  36. 'export_menu' => array(
  37. 'path' => 'admin/config/people/accounts/export',
  38. 'access arguments' => 'administer users',
  39. ),
  40. 'import_menu' => array(
  41. 'path' => 'admin/config/people/accounts/import',
  42. 'access arguments' => 'administer users',
  43. ),
  44. );
  45. if (module_exists('taxonomy')) {
  46. $info['taxonomy_term'] = array(
  47. 'bundle_export_callback' => '_bc_copy_taxonomy_load',
  48. 'bundle_save_callback' => '_bc_copy_taxonomy_save',
  49. 'export_menu' => array(
  50. 'path' => 'admin/structure/taxonomy/export',
  51. 'access arguments' => 'administer taxonomy',
  52. ),
  53. 'import_menu' => array(
  54. 'path' => 'admin/structure/taxonomy/import',
  55. 'access arguments' => 'administer taxonomy',
  56. ),
  57. );
  58. }
  59. return $info;
  60. }
  61. /**
  62. * Implements hook_menu().
  63. */
  64. function bundle_copy_menu() {
  65. $items = array();
  66. $bc_info = bundle_copy_get_info();
  67. foreach ($bc_info as $entity_type => $info) {
  68. $items[$info['export_menu']['path']] = array(
  69. 'title' => 'Export',
  70. 'page callback' => 'drupal_get_form',
  71. 'page arguments' => array('bundle_copy_export', $entity_type),
  72. 'access arguments' => array($info['export_menu']['access arguments']),
  73. 'type' => MENU_LOCAL_TASK
  74. );
  75. $items[$info['import_menu']['path']] = array(
  76. 'title' => 'Import',
  77. 'page callback' => 'drupal_get_form',
  78. 'page arguments' => array('bundle_copy_import', $entity_type),
  79. 'access callback' => 'bundle_copy_import_access',
  80. 'access arguments' => array($info['import_menu']['access arguments']),
  81. 'type' => MENU_LOCAL_TASK
  82. );
  83. }
  84. return $items;
  85. }
  86. /**
  87. * Bundle copy import access callback.
  88. *
  89. * Bundle copy imports require an additional access check because they are PHP
  90. * code and PHP is more locked down than the general permission.
  91. */
  92. function bundle_copy_import_access($permission) {
  93. return user_access($permission) && user_access('use PHP for settings');
  94. }
  95. /**
  96. * Menu callback: present the export page.
  97. */
  98. function bundle_copy_export($form, &$form_state, $entity_type = 'node') {
  99. if (isset($form_state['step'])) {
  100. $step = $form_state['step'];
  101. }
  102. else {
  103. $step = 1;
  104. $form_state['step'] = $step;
  105. }
  106. switch ($step) {
  107. // Select the bundles.
  108. case 1:
  109. $bundles = _bundle_copy_bundle_info($entity_type, TRUE);
  110. $form['bundle-info'] = array(
  111. '#markup' => t('Select bundles you want to export.'),
  112. );
  113. $form['bundles'] = array(
  114. '#type' => 'tableselect',
  115. '#header' => array('label' => t('Bundle')),
  116. '#options' => $bundles,
  117. '#required' => TRUE,
  118. '#empty' => t('No bundles found.'),
  119. );
  120. $form['next'] = array(
  121. '#type' => 'submit',
  122. '#value' => t('Next'),
  123. );
  124. break;
  125. // List the fields / field groups.
  126. case 2:
  127. // Field group.
  128. $all_groups = function_exists('field_group_info_groups') ? field_group_info_groups() : array();
  129. // Fields.
  130. $field_options = $instances = array();
  131. $selected_bundles = $form_state['page_values'][1]['bundles'];
  132. foreach ($selected_bundles as $key => $bundle) {
  133. if ($key === $bundle) {
  134. $instances += field_info_instances($entity_type, $bundle);
  135. }
  136. }
  137. ksort($instances);
  138. foreach ($instances as $key => $info) {
  139. $field_options[$key]['field'] = $info['field_name']; // Same as $key.
  140. $field_options[$key]['label'] = $info['label'];
  141. }
  142. $form['fields-info'] = array(
  143. '#markup' => t('Select fields you want to export.'),
  144. );
  145. $form['fields'] = array(
  146. '#type' => 'tableselect',
  147. '#header' => array('field' => t('Field name'), 'label' => t('Label')),
  148. '#options' => $field_options,
  149. '#empty' => t('No fields found.'),
  150. );
  151. // Field group support.
  152. if (!empty($all_groups)) {
  153. $group_options = $fieldgroups = array();
  154. if (isset($all_groups[$entity_type])) {
  155. foreach ($selected_bundles as $key => $bundle) {
  156. if ($key === $bundle) {
  157. if (!isset($all_groups[$entity_type][$key])) {
  158. continue;
  159. }
  160. foreach ($all_groups[$entity_type][$key] as $view_mode => $groups) {
  161. foreach ($groups as $field_group) {
  162. $group_options[$field_group->id]['fieldgroup'] = $field_group->label . ' (' . $field_group->bundle . ' - ' . $field_group->mode .')';
  163. $fieldgroups[$field_group->id] = $field_group;
  164. }
  165. }
  166. }
  167. }
  168. }
  169. if (!empty($group_options)) {
  170. $form['fieldgroups-info'] = array(
  171. '#markup' => t('Select field groups you want to export.'),
  172. );
  173. $form['fieldgroups'] = array(
  174. '#type' => 'tableselect',
  175. '#header' => array('fieldgroup' => t('Field group name')),
  176. '#options' => $group_options,
  177. );
  178. $form['fieldgroups-full'] = array(
  179. '#type' => 'value',
  180. '#value' => $fieldgroups,
  181. );
  182. }
  183. }
  184. $form['actions'] = array('#type' => 'actions');
  185. $form['actions']['next'] = array(
  186. '#type' => 'submit',
  187. '#value' => t('Export'),
  188. );
  189. $bc_info = bundle_copy_get_info();
  190. $form['actions']['cancel'] = array(
  191. '#markup' => l(t('Cancel'), $bc_info[$entity_type]['export_menu']['path']),
  192. );
  193. break;
  194. // Export data.
  195. case 3:
  196. $data = _bundle_copy_export_data($entity_type, $form_state['page_values']);
  197. $form['export'] = array(
  198. '#title' => t('Export data'),
  199. '#type' => 'textarea',
  200. '#cols' => 60,
  201. '#value' => $data,
  202. '#rows' => 40,
  203. '#description' => t('Copy the export text and paste it into another bundle using the import function.'),
  204. );
  205. break;
  206. }
  207. return $form;
  208. }
  209. /**
  210. * Submit callback: export data.
  211. */
  212. function bundle_copy_export_submit($form, &$form_state) {
  213. // Save the form state values.
  214. $step = $form_state['step'];
  215. $form_state['page_values'][$step] = $form_state['values'];
  216. // Add step and rebuild.
  217. $form_state['step'] = $form_state['step'] + 1;
  218. $form_state['rebuild'] = TRUE;
  219. }
  220. /**
  221. * Menu callback: present the import page.
  222. */
  223. function bundle_copy_import($form, $form_state, $entity_type = 'node') {
  224. $form['entity_type'] = array('#type' => 'value', '#value' => $entity_type);
  225. $form['info'] = array(
  226. '#markup' => t('This form will import bundle and field definitions.'),
  227. );
  228. //$form['type_name'] = array(
  229. // '#title' => t('Bundle'),
  230. // '#description' => t('Select the bundle to import these fields into.<br/>Select &lt;Create&gt; to create a new bundle to contain the fields.'),
  231. // '#type' => 'select',
  232. // '#options' => array('<create>' => t('<Create>')) + _bundle_copy_bundle_info($entity_type),
  233. //);
  234. $form['macro'] = array(
  235. '#type' => 'textarea',
  236. '#rows' => 10,
  237. '#title' => t('Import data'),
  238. '#required' => TRUE,
  239. '#description' => t('Paste the text created by a bundle export into this field.'),
  240. );
  241. $form['submit'] = array(
  242. '#type' => 'submit',
  243. '#value' => t('Import'),
  244. );
  245. return $form;
  246. }
  247. /**
  248. * Submit callback: import data.
  249. */
  250. function bundle_copy_import_submit($form, &$form_state) {
  251. // Evaluate data.
  252. eval($form_state['values']['macro']);
  253. if (isset($data) && is_array($data)) {
  254. $modules = module_list();
  255. $bc_info = bundle_copy_get_info();
  256. // Create bundles.
  257. foreach ($data['bundles'] as $key => $bundle) {
  258. $entity_type = '';
  259. if (is_object($bundle)) {
  260. $entity_type = $bundle->bc_entity_type;
  261. }
  262. elseif (is_array($bundle)) {
  263. $entity_type = $bundle['bc_entity_type'];
  264. }
  265. if (!empty($entity_type)) {
  266. $existing_bundles = _bundle_copy_bundle_info($entity_type);
  267. $bundle_save_callback = $bc_info[$entity_type]['bundle_save_callback'];
  268. $bundle_info = $bundle_save_callback($bundle);
  269. if (!isset($existing_bundles[$key])) {
  270. drupal_set_message(t('%bundle bundle has been created.', array('%bundle' => $bundle->name)));
  271. }
  272. else {
  273. drupal_set_message(t('%bundle bundle has been updated.', array('%bundle' => $bundle->name)));
  274. }
  275. }
  276. }
  277. // Create or update fields and their instances
  278. if (isset($data['fields'])) {
  279. foreach ($data['fields'] as $key => $field) {
  280. // Check if the field module exists.
  281. $module = $field['module'];
  282. if (!isset($modules[$module])) {
  283. drupal_set_message(t('%field_name field could not be created because the module %module is disabled or missing.', array('%field_name' => $key, '%module' => $module)), 'error');
  284. continue;
  285. }
  286. if (isset($data['instances'][$key])) {
  287. // Create or update field.
  288. $prior_field = field_read_field($field['field_name'], array('include_inactive' => TRUE));
  289. if (!$prior_field) {
  290. field_create_field($field);
  291. drupal_set_message(t('%field_name field has been created.', array('%field_name' => $key)));
  292. }
  293. else {
  294. $field['id'] = $prior_field['id'];
  295. field_update_field($field);
  296. drupal_set_message(t('%field_name field has been updated.', array('%field_name' => $key)));
  297. }
  298. // Create or update field instances.
  299. foreach ($data['instances'][$key] as $ikey => $instance) {
  300. // Make sure the needed key exists.
  301. if (!isset($instance['field_name'])) {
  302. continue;
  303. }
  304. $prior_instance = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
  305. if (!$prior_instance) {
  306. field_create_instance($instance);
  307. drupal_set_message(t('%field_name instance has been created for @bundle in @entity_type.', array('%field_name' => $key, '@bundle' => $instance['bundle'], '@entity_type' => $instance['entity_type'])));
  308. }
  309. else {
  310. $instance['id'] = $prior_instance['id'];
  311. $instance['field_id'] = $prior_instance['field_id'];
  312. field_update_instance($instance);
  313. drupal_set_message(t('%field_name instance has been updated for @bundle in @entity_type.', array('%field_name' => $key, '@bundle' => $instance['bundle'], '@entity_type' => $instance['entity_type'])));
  314. }
  315. }
  316. }
  317. }
  318. }
  319. // Create / update fieldgroups.
  320. if (isset($data['fieldgroups'])) {
  321. if (module_exists('field_group')) {
  322. ctools_include('export');
  323. $existing_field_groups = field_group_info_groups();
  324. foreach ($data['fieldgroups'] as $identifier => $fieldgroup) {
  325. if (isset($existing_field_groups[$fieldgroup->entity_type][$fieldgroup->bundle][$fieldgroup->mode][$fieldgroup->group_name])) {
  326. $existing = $existing_field_groups[$fieldgroup->entity_type][$fieldgroup->bundle][$fieldgroup->mode][$fieldgroup->group_name];
  327. $fieldgroup->id = $existing->id;
  328. if (!isset($fieldgroup->disabled)) {
  329. $fieldgroup->disabled = FALSE;
  330. }
  331. ctools_export_crud_save('field_group', $fieldgroup);
  332. ctools_export_crud_set_status('field_group', $fieldgroup, $fieldgroup->disabled);
  333. drupal_set_message(t('%fieldgroup fieldgroup has been updated for @bundle in @entity_type.', array('%fieldgroup' => $fieldgroup->label, '@bundle' => $fieldgroup->bundle, '@entity_type' => $fieldgroup->entity_type)));
  334. }
  335. else {
  336. unset($fieldgroup->id);
  337. unset($fieldgroup->export_type);
  338. if (!isset($fieldgroup->disabled)) {
  339. $fieldgroup->disabled = FALSE;
  340. }
  341. ctools_export_crud_save('field_group', $fieldgroup);
  342. $fieldgroup->export_type = 1;
  343. ctools_export_crud_set_status('field_group', $fieldgroup, $fieldgroup->disabled);
  344. drupal_set_message(t('%fieldgroup fieldgroup has been saved for @bundle in @entity_type.', array('%fieldgroup' => $fieldgroup->label, '@bundle' => $fieldgroup->bundle, '@entity_type' => $fieldgroup->entity_type)));
  345. }
  346. }
  347. }
  348. else {
  349. drupal_set_message(t('The fieldgroups could not be saved because the <em>Field group</em> module is disabled or missing.'), 'error');
  350. }
  351. }
  352. // Clear caches.
  353. field_info_cache_clear();
  354. if (module_exists('field_group')) {
  355. cache_clear_all('field_groups', 'cache_field');
  356. }
  357. }
  358. else {
  359. drupal_set_message(t('The pasted text did not contain any valid export data.'), 'error');
  360. }
  361. }
  362. /**
  363. * Return bundles for a certain entity type.
  364. *
  365. * @param $entity_type
  366. * The name of the entity type.
  367. * @param $table_select
  368. * Whether we're returning for the table select or not.
  369. */
  370. function _bundle_copy_bundle_info($entity_type, $table_select = FALSE) {
  371. static $bundles = array();
  372. if (!isset($bundles[$entity_type])) {
  373. $bundles[$entity_type] = array();
  374. $entity_info = entity_get_info($entity_type);
  375. $entity_bundles = $entity_info['bundles'];
  376. ksort($entity_bundles);
  377. foreach ($entity_bundles as $key => $info) {
  378. $label = isset($info['label']) ? $info['label'] : drupal_ucfirst(str_replace('_', ' ', $key));
  379. if ($table_select) {
  380. $bundles[$entity_type][$key]['label'] = $label;
  381. }
  382. else {
  383. $bundles[$entity_type][$key] = $label;
  384. }
  385. }
  386. }
  387. return $bundles[$entity_type];
  388. }
  389. /**
  390. * Creates export data
  391. *
  392. * @param $entity_type
  393. * The name of the entity_type
  394. * @param $selected_data
  395. * The selected data.
  396. */
  397. function _bundle_copy_export_data($entity_type, $selected_data) {
  398. ctools_include('export');
  399. $bc_info = bundle_copy_get_info();
  400. $selected_bundles = $selected_data[1]['bundles'];
  401. $selected_fields = $selected_data[2]['fields'];
  402. $selected_fieldgroups = isset($selected_data[2]['fieldgroups']) ? $selected_data[2]['fieldgroups'] : array();
  403. $full_fieldgroups = isset($selected_data[2]['fieldgroups-full']) ? $selected_data[2]['fieldgroups-full'] : array();
  404. $data = $instances = array();
  405. $fields = field_info_fields();
  406. foreach ($selected_bundles as $bkey => $binfo) {
  407. if ($bkey !== $binfo) {
  408. continue;
  409. }
  410. $field_instances = field_info_instances($entity_type, $bkey);
  411. ksort($field_instances);
  412. // Bundles export data.
  413. $bundle_info_callback = $bc_info[$entity_type]['bundle_export_callback'];
  414. $bundle_info = $bundle_info_callback($bkey, $entity_type);
  415. if (is_object($bundle_info)) {
  416. $bundle_info->bc_entity_type = $entity_type;
  417. }
  418. elseif (is_array($bundle_info)) {
  419. $bundle_info['bc_entity_type'] = $entity_type;
  420. }
  421. $data['bundles'][$bkey] = $bundle_info;
  422. // Fields export data.
  423. foreach ($selected_fields as $fkey => $finfo) {
  424. if ($fkey === $finfo) {
  425. if (!isset($data['fields'][$fkey])) {
  426. unset($fields[$fkey]['id']);
  427. $data['fields'][$fkey] = $fields[$fkey];
  428. }
  429. if (isset($field_instances[$fkey])) {
  430. unset($field_instances[$fkey]['id']);
  431. unset($field_instances[$fkey]['field_id']);
  432. $instances[$fkey][] = $field_instances[$fkey];
  433. }
  434. }
  435. }
  436. }
  437. ksort($instances);
  438. $data['instances'] = $instances;
  439. // Field group export data.
  440. if (!empty($selected_fieldgroups)) {
  441. foreach ($selected_fieldgroups as $key => $value) {
  442. if ($value !== 0) {
  443. $data['fieldgroups'][$full_fieldgroups[$key]->identifier] = $full_fieldgroups[$key];
  444. }
  445. }
  446. }
  447. return '$data = ' . ctools_var_export($data) . ';';
  448. }
  449. /**
  450. * Helper function to load the taxonomy, but remove the vid on the object.
  451. *
  452. * @param $name
  453. * The name of the bundle.
  454. */
  455. function _bc_copy_taxonomy_load($name) {
  456. $bundle = taxonomy_vocabulary_machine_name_load($name);
  457. return $bundle;
  458. }
  459. /**
  460. * Helper function to save the taxonomy.
  461. */
  462. function _bc_copy_taxonomy_save($bundle) {
  463. if ($bundle->vid) {
  464. unset($bundle->vid);
  465. }
  466. $vid = db_query('SELECT vid FROM {taxonomy_vocabulary} WHERE machine_name = :machine_name', array(':machine_name' => $bundle->machine_name))->fetchField();
  467. if ($vid) {
  468. $bundle->vid = $vid;
  469. }
  470. taxonomy_vocabulary_save($bundle);
  471. }
  472. /**
  473. * Helper function to ignore a bundle on export.
  474. */
  475. function _bc_bundle_export_ignore($name) {
  476. }
  477. /**
  478. * Helper function to ignore a bundle save.
  479. */
  480. function _bc_bundle_save_ignore($bundle) {
  481. }