ftools.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <?php
  2. /**
  3. * @file
  4. * ftools module
  5. */
  6. /**
  7. * Implements hook_js_alter().
  8. */
  9. function ftools_js_alter(&$javascript) {
  10. $item = menu_get_item();
  11. if (isset($item['path']) && $item['path'] === 'admin/structure/features') {
  12. $js_path = drupal_get_path('module', 'features') . '/features.js';
  13. if (isset($javascript[$js_path])) {
  14. if (variable_get('ftools_disable_features_page_js', 0)) {
  15. unset($javascript[$js_path]);
  16. }
  17. }
  18. }
  19. }
  20. /**
  21. * Implements hook_permission().
  22. *
  23. * @return array An array of valid permissions for the ftools module
  24. */
  25. function ftools_permission() {
  26. return array(
  27. 'execute unlink files' => array(
  28. 'title' => t('execute unlink files'),
  29. 'description' => t('TODO Add a description for \'execute unlink files\''),
  30. ),
  31. );
  32. }
  33. /**
  34. * Implements hook_menu().
  35. *
  36. * @return An array of menu items.
  37. */
  38. function ftools_menu() {
  39. $items = array();
  40. $items['admin/structure/features/unlink'] = array(
  41. 'title' => 'Execute unlink files',
  42. 'page callback' => 'drupal_get_form',
  43. 'page arguments' => array('ftools_unlink_form'),
  44. 'access arguments' => array('execute unlink files'),
  45. );
  46. return $items;
  47. }
  48. /**
  49. * Implements hook_form_alter().
  50. */
  51. function ftools_form_features_admin_form_alter(&$form, &$form_state) {
  52. $form['disable_ajax'] = array('#type' => 'checkbox', '#title' => t('disable the features status ajax loading'), '#default_value' => variable_get('ftools_disable_features_page_js', 0));
  53. $form['buttons']['submit']['#submit'][] = 'ftools_features_admin_form_submit';
  54. $form['buttons']['revert'] = array(
  55. '#type' => 'submit',
  56. '#value' => t('Revert them All'),
  57. '#attributes' => array('onclick' => 'if(!confirm("Are you sure you want to revert all features? \nThis action cannot be undone.")){return false;}'),
  58. '#submit' => array('ftools_features_revert_all_submit')
  59. );
  60. if (variable_get('ftools_disable_features_page_js', 0) == 0) {
  61. return;
  62. }
  63. drupal_add_css('table.features .admin-check, table.features .admin-default, table.features .admin-overridden, table.features .admin-rebuilding, table.features .admin-needs-review {
  64. display: inline!important;}', array('type' => 'inline'));
  65. $groups = array();
  66. foreach ($form['#features'] as $feature) {
  67. if (isset($feature->info['package'])) {
  68. $package_title = !empty($feature->info['package']) ? $feature->info['package'] : t('Other');
  69. $package = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $package_title));
  70. if (in_array($package, $groups)) {
  71. continue;
  72. }
  73. $groups[] = $package;
  74. }
  75. }
  76. foreach ($groups as $group) {
  77. foreach ($form[$group]['state'] as $feature_name => &$feature_status) {
  78. if ($form[$group]['status'][$feature_name]['#default_value'] == 0) {
  79. continue;
  80. }
  81. $status = features_get_storage($feature_name);
  82. $cls = "admin";
  83. switch ($status) {
  84. case 3:
  85. $cls .= "-rebuilding";
  86. $txt = "Rebuilding";
  87. break;
  88. case 2:
  89. $cls .= "-needs-review";
  90. $txt = "Needs review";
  91. break;
  92. case 1:
  93. $txt = "Overridden";
  94. $cls .= "-overridden";
  95. break;
  96. default:
  97. $txt = "Default";
  98. $cls .= "-default";
  99. break;
  100. }
  101. $cls .= " features-storage";
  102. $feature_status['#markup'] = l($txt, "admin/structure/features/$feature_name", array('attributes' => array('class' => $cls)));
  103. }
  104. }
  105. }
  106. /**
  107. * Implements hook_form_FORM_ID_alter().
  108. */
  109. function ftools_form_features_export_form_alter(&$form, &$form_state) {
  110. if (isset($form['#feature']) && is_object($form['#feature']) && isset($form['#feature']->name)) {
  111. $path = drupal_get_path('module', $form['#feature']->name);
  112. }
  113. else {
  114. $path = 'sites/all/modules';
  115. }
  116. $writeable = file_prepare_directory($path);
  117. if ($writeable === FALSE) {
  118. drupal_set_message(t('The directory @path does not exists or not writable', array('@path' => $path)), 'error');
  119. }
  120. //TODO: add backup options.
  121. if (variable_get('ftools_disable_features_page_js', 0)) {
  122. foreach ($form['export']['sources'] as &$value) {
  123. if (is_array($value) && isset($value['#ajax'])) {
  124. unset($value['#ajax']);
  125. }
  126. }
  127. }
  128. $form['buttons']['create'] = array(
  129. '#type' => 'submit',
  130. '#value' => t('Auto create feature'),
  131. '#weight' => 10,
  132. '#submit' => array('ftools_export_build_form_submit'),
  133. );
  134. $form['ftools_dest'] = array(
  135. '#title' => t('Custom module path.'),
  136. '#type' => 'textfield',
  137. '#default_value' => $path
  138. );
  139. if ($form['#feature']) {
  140. $form['buttons']['safe_recreate'] = array(
  141. '#type' => 'submit',
  142. '#value' => t('Safe Auto create feature'),
  143. '#weight' => 10,
  144. '#submit' => array('ftools_form_features_export_form_safe_submit'),
  145. );
  146. }
  147. }
  148. /**
  149. * @todo Please document this function.
  150. * @see http://drupal.org/node/1354
  151. */
  152. function ftools_form_features_export_form_safe_submit($form, &$form_state) {
  153. if (module_exists('rules')) {
  154. module_load_include('inc', 'features', 'features.export');
  155. features_include();
  156. module_load_include('inc', 'rules', 'modules/events');
  157. rules_invoke_event('init');
  158. module_load_include('inc', 'rules_admin', 'rules_admin.export');
  159. module_load_include('inc', 'rules', 'rules.export');
  160. }
  161. $destination = $form_state['values']['ftools_dest'];
  162. $arr = explode('/', $destination);
  163. $arr[sizeof($arr) - 1] .= '_unlink';
  164. $module_name = $arr[sizeof($arr) - 1];
  165. $destination = implode('/', $arr) . '/';
  166. $destination_info = _ftools_get_unlink_version($destination);
  167. $hooks = _ftools_get_hooks();
  168. $new_feature = $form_state['values']['sources'];
  169. $old_feature = $form['#feature']->info['features'];
  170. $stub = array();
  171. foreach ($hooks as $hook) {
  172. if (isset($old_feature[$hook])) {
  173. foreach ($old_feature[$hook] as $value) {
  174. if (!$new_feature[$hook][$value]) {
  175. $stub[$hook][] = $value;
  176. }
  177. }
  178. }
  179. }
  180. if ($stub) {
  181. $file_content = array();
  182. foreach ($stub as $hook => $value) {
  183. $output = array();
  184. $code_buffer = '';
  185. foreach ($value as $component) {
  186. switch ($hook) {
  187. case 'views_view':
  188. $view = views_get_view($component);
  189. $output[] = $view->export();
  190. $output[] = '$view->save();';
  191. break;
  192. case 'box':
  193. $export = array('features' => array('box' => array($component => $component)));
  194. $component_hooks = features_export_render_hooks($export, $module_name, TRUE);
  195. $output[] = str_replace(' return $export;', '', $component_hooks['box']['default_box']);
  196. $output[] = 'drupal_write_record(' . "'box'" . ', $box);';
  197. break;
  198. }
  199. }
  200. $code_buffer .= implode("\n", $output);
  201. $output = array();
  202. $output[] = "/**";
  203. $output[] = " * Unlink {$module_name}_{$destination_info['delta']}_{$hook}().";
  204. $output[] = " */";
  205. $output[] = "function {$module_name}_{$destination_info['delta']}_{$hook}() {";
  206. $output[] = $code_buffer;
  207. $output[] = "}";
  208. $file_content[] = implode("\n", $output);
  209. }
  210. $file_content = "<?php\n\n" . implode("\n\n", $file_content) . "\n";
  211. mkdir($destination_info['destination'], 0777, TRUE);
  212. $error = '';
  213. if (file_put_contents("{$destination_info['destination']}$module_name.{$destination_info['delta']}.unlink.inc", $file_content) === FALSE) {
  214. $error = TRUE;
  215. }
  216. if ($error) {
  217. drupal_set_message(t('One or more files could not be written to the file system. This is probably a permission issue. See !link for more information.', array('!link' => l(t('the Features Tools module page'), 'http://drupal.org/project/ftools'))), 'error');
  218. return;
  219. }
  220. else {
  221. drupal_set_message(t('The unlink file was successfully created.'));
  222. }
  223. }
  224. ftools_export_build_form_submit($form, $form_state);
  225. }
  226. /**
  227. * submit function for the features_export_form, write the file to disk.
  228. */
  229. function ftools_export_build_form_submit($form, &$form_state) {
  230. module_load_include('inc', 'features', 'features.export');
  231. features_include();
  232. // Assemble the combined component list.
  233. $stub = array();
  234. $components = array_keys(features_get_components());
  235. foreach ($components as $component) {
  236. // User-selected components take precedence.
  237. if (!empty($form_state['values']['sources'][$component])) {
  238. $stub[$component] = features_dom_decode_options(array_filter($form_state['values']['sources'][$component]));
  239. }
  240. // Only fallback to an existing feature's values if there are no export options for the component.
  241. elseif (!empty($form['#feature']->info['features'][$component])) {
  242. $stub[$component] = $form['#feature']->info['features'][$component];
  243. }
  244. }
  245. // Generate populated feature.
  246. $module_name = $form_state['values']['module_name'];
  247. $export = features_populate($stub, $form_state['values']['sources']['dependencies'], $module_name);
  248. // Directly copy the following attributes.
  249. $attr = array('name', 'description');
  250. foreach ($attr as $key) {
  251. $export[$key] = isset($form_state['values'][$key]) ? $form_state['values'][$key] : NULL;
  252. }
  253. // If either update status-related keys are provided, add a project key
  254. // corresponding to the module name.
  255. if (!empty($form_state['values']['version']) || !empty($form_state['values']['project_status_url'])) {
  256. $export['project'] = $form_state['values']['module_name'];
  257. }
  258. if (!empty($form_state['values']['version'])) {
  259. $export['version'] = $form_state['values']['version'];
  260. }
  261. if (!empty($form_state['values']['project_status_url'])) {
  262. $export['project status url'] = $form_state['values']['project_status_url'];
  263. }
  264. // Generate download.
  265. $directory = '';
  266. $destination = $form_state['values']['ftools_dest'];
  267. if ($destination) {
  268. $directory .= $destination . '/';
  269. }
  270. //TODO: find a better term then arg(3) to find if we are on create page or recreat.
  271. if (arg(3) == 'create') {
  272. $directory .= $module_name;
  273. }
  274. if ($files = features_export_render($export, $module_name, TRUE)) {
  275. //If the directory dont exists create it.
  276. if (!file_exists($directory)) {
  277. mkdir($directory, 0777, TRUE);
  278. }
  279. $error = false;
  280. foreach ($files as $extension => $file_contents) {
  281. if (!in_array($extension, array('module', 'info'))) {
  282. $extension .= '.inc';
  283. }
  284. if (file_put_contents("{$directory}/{$module_name}.$extension", $file_contents) === FALSE) {
  285. $error = true;
  286. }
  287. }
  288. if ($error) {
  289. drupal_set_message(t('One or more files could not be written to the file system. This is probably a permission issue. See !link for more information.', array('!link' => l(t('the Features Tools module page'), 'http://drupal.org/project/ftools'))), 'error');
  290. }
  291. else {
  292. drupal_set_message(t('The feature was successfully created.'));
  293. }
  294. }
  295. drupal_flush_all_caches();
  296. }
  297. /**
  298. * @todo Please document this function.
  299. * @see http://drupal.org/node/1354
  300. */
  301. function ftools_unlink_form($form, $form_state) {
  302. $files = drupal_system_listing('/\.unlink.inc$/', 'modules', 'name', 0);
  303. $form = array();
  304. $options = array();
  305. foreach ($files as $value) {
  306. $options[$value->name] = str_replace('_unlink.unlink', '', $value->name);
  307. }
  308. $form['unlink_options'] = array(
  309. '#type' => 'checkboxes',
  310. '#options' => $options,
  311. );
  312. $form['submit'] = array(
  313. '#type' => 'submit',
  314. '#value' => 'submit',
  315. '#name' => 'submit',
  316. );
  317. return $form;
  318. }
  319. /**
  320. * @todo Please document this function.
  321. * @see http://drupal.org/node/1354
  322. */
  323. function ftools_unlink_form_submit($form, &$form_state) {
  324. $hooks = _ftools_get_hooks();
  325. $files = drupal_system_listing('/\.unlink.inc$/', 'modules', 'name', 0);
  326. foreach ($form_state['values']['unlink_options'] as $key => $value) {
  327. if ($value) {
  328. include_once DRUPAL_ROOT . "/" . $files[$key]->uri;
  329. views_include('view');
  330. foreach ($hooks as $hook) {
  331. $function_name = str_replace('.unlink', '', $key) . "_$hook";
  332. $function_name = str_replace('.', '_', $function_name);
  333. if (function_exists($function_name)) {
  334. //TODO add info file so we can check if the element is in the db before trying to insert it.
  335. //i add a try catch block meanwhile to hide the error message.
  336. try {
  337. include_once DRUPAL_ROOT . "/" . $files[$key]->uri;
  338. } catch (Exception $e) {
  339. watchdog('ftools', "error in unlink function:" . $e->getMessage());
  340. }
  341. drupal_set_message("$hook $function_name");
  342. }
  343. }
  344. }
  345. }
  346. }
  347. function _ftools_get_hooks() {
  348. return array('box', 'views_view');
  349. }
  350. /**
  351. * Get the next version for the unlink directory.
  352. * @param string $destination , the $destination path.
  353. * @return array with the directory delta and the new destination.
  354. */
  355. function _ftools_get_unlink_version($destination) {
  356. $destination = substr($destination, 0, strlen($destination) - 1);
  357. $i = 1;
  358. while (file_exists("{$destination}_$i")) {
  359. $i++;
  360. }
  361. return array('destination' => "{$destination}_$i/", 'delta' => $i);
  362. }
  363. function ftools_features_admin_form_submit($form, &$form_state) {
  364. variable_set('ftools_disable_features_page_js', $form_state['values']['disable_ajax']);
  365. }
  366. function ftools_features_revert_all_submit($form, &$form_state) {
  367. module_load_include('inc', 'features', 'features.export');
  368. features_include();
  369. features_revert();
  370. $form_state['redirect'] = 'admin/structure/features';
  371. }