flag.export.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <?php
  2. /**
  3. * @file
  4. * Import/Export functionality provided by Flag module.
  5. */
  6. /**
  7. * Export a flag to code.
  8. *
  9. * @param array $flags
  10. * An array of flag objects, or flag name.
  11. * @param string $module
  12. * Optional. The name of the module that will be created if exporting to use
  13. * in hook_flag_default_flags().
  14. */
  15. function flag_export_flags($flags = array(), $module = '', $indent = '') {
  16. // For features_var_export() (optional).
  17. module_load_include('inc', 'features', 'features.export');
  18. $output = $indent . '$flags = array();' . "\n";
  19. foreach ($flags as $item) {
  20. if (is_object($item)) {
  21. $flag = $item;
  22. }
  23. else {
  24. // We got just the flag name, for example from the features
  25. // implementation.
  26. if (!($flag = flag_load($item, TRUE))) {
  27. continue;
  28. }
  29. }
  30. if (!$flag->is_compatible()) {
  31. drupal_set_message(t('Could not export flag %flag-name: Your flag was created by a different version of the Flag module than is now being used.', array('%flag-name' => $flag->name)), 'error');
  32. continue;
  33. }
  34. $flag->api_version = FLAG_API_VERSION;
  35. $new_flag = (array) $flag;
  36. if (!empty($module)) {
  37. // Even though Flag adds the module name itself later, we add the module
  38. // name here for reference by other modules (such as Features).
  39. $new_flag['module'] = $module;
  40. // Lock the flag name, as is normally desired by modules using
  41. // hook_flag_default_flags(), and needed by Features.
  42. $new_flag['locked'] = array('name');
  43. }
  44. // Allow other modules to change the exported flag.
  45. drupal_alter('flag_export', $new_flag);
  46. // Remove properties we don't export.
  47. $unset_properties = array(
  48. // Remove the flag ID.
  49. 'fid',
  50. // The name is emitted as the key for the array.
  51. 'name',
  52. // The entity info is just used as helper data.
  53. 'entity_info',
  54. // Remove roles.
  55. 'roles',
  56. // Remove errors.
  57. 'errors',
  58. );
  59. foreach ($unset_properties as $property) {
  60. unset($new_flag[$property]);
  61. }
  62. $output .= $indent . '// Exported flag: "' . check_plain($flag->get_title()) . '"' . ".\n";
  63. $output .= $indent . '$flags[\'' . $flag->name . '\'] = ' . (function_exists('features_var_export') ? features_var_export($new_flag, $indent) : var_export($new_flag, TRUE)) . ";\n";
  64. }
  65. $output .= $indent . 'return $flags;' . "\n";
  66. return $output;
  67. }
  68. /**
  69. * Form to import a flag.
  70. */
  71. function flag_import_form() {
  72. $form = array();
  73. $form['import'] = array(
  74. '#title' => t('Flag import code'),
  75. '#type' => 'textarea',
  76. '#default_value' => '',
  77. '#rows' => 15,
  78. '#required' => TRUE,
  79. '#description' => t('Paste the code from a <a href="@export-url">flag export</a> here to import it into you site. Flags imported with the same name will update existing flags. Flags with a new name will be created.', array('@export-url' => url(FLAG_ADMIN_PATH . '/export'))),
  80. );
  81. $form['submit'] = array(
  82. '#value' => t('Import'),
  83. '#type' => 'submit',
  84. );
  85. return $form;
  86. }
  87. /**
  88. * Validate handler; Import a flag.
  89. */
  90. function flag_import_form_validate($form, &$form_state) {
  91. $flags = array();
  92. ob_start();
  93. eval($form_state['values']['import']);
  94. ob_end_clean();
  95. if (!isset($flags) || !is_array($flags)) {
  96. form_set_error('import', t('A valid list of flags could not be found in the import code.'));
  97. return;
  98. }
  99. // Create the flag object.
  100. foreach ($flags as $flag_name => $flag_info) {
  101. // Backward compatibility: old exported flags have their names in $flag_info
  102. // instead, so we use the += operator to not overwrite it.
  103. $flag_info += array(
  104. 'name' => $flag_name,
  105. );
  106. $new_flag = flag_flag::factory_by_array($flag_info);
  107. // Give new flags with the same name a matching FID, which tells Flag to
  108. // update the existing flag, rather than creating a new one.
  109. if ($existing_flag = flag_get_flag($new_flag->name)) {
  110. $new_flag->fid = $existing_flag->fid;
  111. }
  112. if ($errors = $new_flag->validate()) {
  113. $message = t('The import of the %flag flag failed because the following errors were encountered during the import:', array('%flag' => $new_flag->name));
  114. $message_errors = array();
  115. foreach ($errors as $field => $field_errors) {
  116. foreach ($field_errors as $error) {
  117. $message_errors[] = $error['message'];
  118. }
  119. }
  120. form_set_error('import', $message . theme('item_list', array('items' => $message_errors)));
  121. }
  122. else {
  123. // Save the new flag for the submit handler.
  124. $form_state['flags'][] = $new_flag;
  125. }
  126. }
  127. }
  128. /**
  129. * Submit handler; Import a flag.
  130. */
  131. function flag_import_form_submit($form, &$form_state) {
  132. module_load_include('inc', 'flag', 'includes/flag.admin');
  133. // Build up values for the cache clear.
  134. $entity_types = array();
  135. $new = FALSE;
  136. foreach ($form_state['flags'] as $flag) {
  137. $flag->save();
  138. if (!empty($flag->status)) {
  139. $flag->enable();
  140. }
  141. if ($flag->is_new) {
  142. drupal_set_message(t('Flag @name has been imported.', array('@name' => $flag->name)));
  143. $new = TRUE;
  144. }
  145. else {
  146. drupal_set_message(t('Flag @name has been updated.', array('@name' => $flag->name)));
  147. }
  148. $entity_types[] = $flag->entity_type;
  149. }
  150. _flag_clear_cache($entity_types, $new);
  151. $form_state['redirect'] = FLAG_ADMIN_PATH;
  152. }
  153. /**
  154. * Export a flag and display it in a form.
  155. */
  156. function flag_export_form($form, &$form_state, $flag = NULL) {
  157. // If we were passed a flag, use it as the list of flags to export.
  158. if ($flag) {
  159. $flags = array($flag);
  160. }
  161. // Display a list of flags to export.
  162. if (!isset($flags)) {
  163. if (isset($form_state['values']['flags'])) {
  164. $flags = array();
  165. foreach ($form_state['values']['flags'] as $flag_name) {
  166. if ($flag_name && $flag = flag_get_flag($flag_name)) {
  167. $flags[] = $flag;
  168. }
  169. }
  170. }
  171. else {
  172. $form['flags'] = array(
  173. '#type' => 'checkboxes',
  174. '#title' => t('Flags to export'),
  175. '#options' => drupal_map_assoc(array_keys(flag_get_flags())),
  176. '#description' => t('Exporting your flags is useful for moving flags from one site to another, or when including your flag definitions in a module.'),
  177. );
  178. $form['submit'] = array(
  179. '#type' => 'submit',
  180. '#value' => t('Export'),
  181. );
  182. }
  183. }
  184. if (isset($flags)) {
  185. $code = flag_export_flags($flags);
  186. // Link to the Features page if module is present, otherwise link to the
  187. // Drupal project page.
  188. $features_link = module_exists('features') ? url('admin/build/features') : url('http://drupal.org/project/features');
  189. $form['export'] = array(
  190. '#type' => 'textarea',
  191. '#title' => t('Flag exports'),
  192. '#description' => t('Use the exported code to later <a href="@import-flag">import</a> it. Exports can be included in modules using <a href="http://drupal.org/node/305086#default-flags">hook_flag_default_flags()</a> or using the <a href="@features-url">Features</a> module.', array('@import-flag' => url(FLAG_ADMIN_PATH . '/import'), '@features-url' => $features_link)),
  193. '#value' => $code,
  194. '#rows' => 15,
  195. );
  196. }
  197. return $form;
  198. }
  199. /**
  200. * Submit handler; Rebuild the export form after the list of flags has been set.
  201. */
  202. function flag_export_form_submit($form, &$form_state) {
  203. $form_state['rebuild'] = TRUE;
  204. }
  205. /**
  206. * Page for displaying an upgrade message and export form for Flag 1.x flags.
  207. */
  208. function flag_update_page($flag) {
  209. if ($flag->is_compatible()) {
  210. drupal_set_message(t('The flag %name is already up-to-date with the latest Flag API and does not need upgrading.', array('%name' => $flag->name)));
  211. drupal_goto(FLAG_ADMIN_PATH);
  212. }
  213. drupal_set_message(t('The flag %name is currently using the Flag API version @version, which is not compatible with the current version of Flag. You can upgrade this flag by pasting the below code into <em>@module_flag_default_flags()</em> function in the @module.module file.', array('%name' => $flag->name, '@version' => $flag->api_version, '@module' => $flag->module)), 'warning');
  214. flag_update_export($flag);
  215. return drupal_get_form('flag_export_form', $flag);
  216. }
  217. /**
  218. * Update a flag before export.
  219. *
  220. * @param flag_flag $flag
  221. * The flag object passed by reference.
  222. */
  223. function flag_update_export(&$flag) {
  224. // Set the API version to 1 by default: version 1 did not explicitly define
  225. // the API version.
  226. if (empty($flag->api_version)) {
  227. $flag->api_version = 1;
  228. }
  229. // Get all our update classes.
  230. // This is not terribly graceful, but the alternative is declaring our classes
  231. // explicitly, or registering them with the Drupal autoloader and then running
  232. // a database query, which seems a waste of space given we only ever need
  233. // these here.
  234. $classes = get_declared_classes();
  235. $update_handlers = array();
  236. foreach ($classes as $class) {
  237. // Any class whose name is of the form 'FlagUpdate_foo' is one of ours, we
  238. // assume. Should this prove problematic, we can add use of reflection here.
  239. if (substr($class, 0, 11) == 'FlagUpdate_') {
  240. // @todo: change this to work with the static class when we drop support
  241. // for PHP 5.2: see commit d5b517.
  242. $update_handler = new $class();
  243. // Cast to string, as decimals as array keys seem to be rounded down to
  244. // ints, WTF PHP?
  245. $version = (string) $update_handler->old_api_version;
  246. $update_handlers[$version] = $update_handler;
  247. }
  248. }
  249. // Sort the classes by old version number.
  250. uksort($update_handlers, 'version_compare');
  251. // Work through each update handler.
  252. foreach ($update_handlers as $old_api_version => $update_handler) {
  253. // Skip update classes that are older than our current flag.
  254. if (version_compare($old_api_version, $flag->api_version, '<')) {
  255. continue;
  256. }
  257. // Run the update and change the API version on the flag.
  258. $update_handler->update($flag);
  259. $flag->api_version = $update_handler->new_api_version;
  260. }
  261. }
  262. /**
  263. * Flag update class for API 1 flags -> API 2.
  264. *
  265. * The class name after the prefix is immaterial, though we follow the Drupal
  266. * system update convention whereby the number here is what we update to.
  267. */
  268. class FlagUpdate_2 {
  269. /**
  270. * The API version this class updates a flag from.
  271. *
  272. * @todo: Change this to a class constant when we drop support for PHP 5.2.
  273. */
  274. public $old_api_version = 1;
  275. /**
  276. * The API version this class updates a flag to.
  277. */
  278. public $new_api_version = 2;
  279. /**
  280. * The update function for the flag.
  281. */
  282. static function update(&$flag) {
  283. if (isset($flag->roles) && !isset($flag->roles['flag'])) {
  284. $flag->roles = array(
  285. 'flag' => $flag->roles,
  286. 'unflag' => $flag->roles,
  287. );
  288. }
  289. }
  290. }
  291. /**
  292. * Flag update class for API 2 flags -> API 3.
  293. */
  294. class FlagUpdate_3 {
  295. public $old_api_version = 2;
  296. public $new_api_version = 3;
  297. static function update(&$flag) {
  298. // Change the content_type property to entity_type.
  299. if (isset($flag->content_type)) {
  300. $flag->entity_type = $flag->content_type;
  301. unset($flag->content_type);
  302. }
  303. // We can't convert the flag roles data to user permissions at this point
  304. // because the flag is disabled and hence hook_permission() doesn't see it
  305. // to define its permissions.
  306. // Instead, we copy it to import_roles, which the flag add form will handle
  307. // on new flags (which this flag will behave as when it is re-enabled).
  308. // @see flag_form()
  309. if (isset($flag->roles)) {
  310. $flag->import_roles = $flag->roles;
  311. }
  312. // Update show_on_teaser property to use new view mode settings.
  313. if (!empty($flag->show_on_teaser)) {
  314. $flag->show_in_links['teaser'] = TRUE;
  315. unset($flag->show_on_teaser);
  316. }
  317. // Update show_on_page property to use new view mode settings.
  318. if (!empty($flag->show_on_page)) {
  319. $flag->show_in_links['full'] = TRUE;
  320. unset($flag->show_on_page);
  321. }
  322. // Update show_on_comment and show_on_entity properties to use new view
  323. // mode settings. Since the old logic was to show on all view modes, do
  324. // that.
  325. if (!empty($flag->show_on_entity) || !empty($flag->show_on_comment)) {
  326. if ($entity_info = entity_get_info($flag->entity_type)) {
  327. foreach ($entity_info['view modes'] as $view_mode => $value) {
  328. $flag->show_in_links[$view_mode] = TRUE;
  329. }
  330. }
  331. unset($flag->show_on_entity, $flag->show_on_comment);
  332. }
  333. }
  334. }