tripal.extensions.inc

File

tripal/includes/tripal.extensions.inc
View source
  1. <?php
  2. function tripal_extensions_form($form, &$form_state = NULL) {
  3. // Get the RSS feed XML from the tripal.info website and save it to
  4. // a temp file so that we don't have to keep pulling the XML
  5. // everythime the page is loaded. If the temp file is older than 1 hours
  6. // then we'll pull it again. The number of seconds in an hour is 3600.
  7. $tmp_file = sys_get_temp_dir() . '/tripal_rss_extensions.xml';
  8. if (!file_exists($tmp_file) or time() - filemtime($tmp_file) > 3600) {
  9. $content = file_get_contents("http://tripal.info/rss/extensions.xml");
  10. file_put_contents($tmp_file, $content);
  11. }
  12. else {
  13. $content = file_get_contents($tmp_file);
  14. }
  15. $xml = new SimpleXmlElement($content);
  16. $namespace = "http://tripal.info/rss/extensions/";
  17. $tab = array_key_exists('tab', $_GET) ? $_GET['tab'] : '';
  18. // Get any filters by categories.
  19. $filters = array();
  20. if (array_key_exists('values', $form_state)) {
  21. foreach ($form_state['values'] as $key => $value) {
  22. // Get the category to be filtered on.
  23. $matches = array();
  24. if (preg_match('/^categories-(.*?)$/', $key, $matches)) {
  25. if ($value == 'any') {
  26. continue;
  27. }
  28. $filters[$matches[1]] = $value;
  29. }
  30. }
  31. }
  32. // Parse the items into an array indexed by type and compatible versions.
  33. $extensions = array();
  34. $types = array();
  35. $type_ids = array();
  36. $categories = array();
  37. $cvs = array();
  38. $tvs = array();
  39. foreach ($xml->channel->item as $extension) {
  40. // Get the type of extension, convert that into a machine-readable name,
  41. // and build the array of types.
  42. $type = (string) $extension->category;
  43. $type_id = preg_replace('/[^\w]/','_', strtolower($type));
  44. $type_ids[$type] = $type_id;
  45. if (!in_array($type_id, $types)) {
  46. $types[$type] = 1;
  47. }
  48. // Get the categories list for this item.
  49. $cats = preg_split('/, /', (string) $extension->children($namespace)->categories);
  50. // Get the guid for this extension
  51. $guid = (string) $extension->guid;
  52. // In order to get fields in the 'tripal_extension' name space we must
  53. // pass in the $namespace to the children function. We first get the
  54. // Tripal versions, then the chado versions and organize the elements
  55. // accordintly.
  56. $tvs_temp = preg_split('/, /', (string) $extension->children($namespace)->tripal_version);
  57. foreach($tvs_temp as $tv) {
  58. $tvs[$tv] = 1;
  59. $cvs_temp = preg_split('/, /', (string) $extension->children($namespace)->chado_version);
  60. foreach($cvs_temp as $cv) {
  61. $cvs[$cv] = 1;
  62. // Keep track of the categories this item has been assigned.
  63. foreach ($cats as $cat) {
  64. $categories[$tv][$cv][$type][$cat] = 1;
  65. $categories['any'][$cv][$type][$cat] = 1;
  66. $categories[$tv]['any'][$type][$cat] = 1;
  67. $categories['any']['any'][$type][$cat] = 1;
  68. }
  69. // If there are filters then only include extensions that match the filters.
  70. if (array_key_exists($type_id, $filters) and !in_array($filters[$type_id], $cats)) {
  71. continue;
  72. }
  73. // Index the items by type, tripal version and chado version.
  74. $item = array();
  75. foreach ($extension->children() as $child) {
  76. $item[$child->getName()] = (string) $child;
  77. }
  78. foreach ($extension->children($namespace) as $child) {
  79. $item[$namespace][$child->getName()] = (string) $child;
  80. }
  81. $extensions[$tv][$cv][$type][$guid] = $item;
  82. $extensions['any'][$cv][$type][$guid] = $item;
  83. $extensions[$tv]['any'][$type][$guid] = $item;
  84. $extensions['any']['any'][$type][$guid] = $item;
  85. }
  86. }
  87. }
  88. // Convert the arrays from an associative array into a normal array, and sort.
  89. $types = array_keys($types);
  90. sort($types);
  91. $cvs = array_keys($cvs);
  92. sort($cvs);
  93. $tvs = array_keys($tvs);
  94. sort($tvs);
  95. // Get the Chado version and convert to the expected format
  96. $chado_version = chado_get_version(TRUE);
  97. $chado_version = preg_replace('/^(\d\.\d).*$/', "v$1x", $chado_version);
  98. $my_chado_version = $chado_version;
  99. // The default value can come from the pager links (thus via $_GET) or
  100. // via ajax call (thus via $form_state).
  101. if (array_key_exists('cv', $_GET)) {
  102. $chado_version = $_GET['cv'];
  103. }
  104. if (array_key_exists('values', $form_state) and array_key_exists('cv', $form_state['values'])) {
  105. $chado_version = $form_state['values']['cv'];
  106. }
  107. // Get the Tripal version. This is the version set in the tripal.info
  108. $info = system_get_info('module', 'tripal');
  109. $tripal_version = $info['version'];
  110. $tripal_version = preg_replace('/^.*?-(\d\.\d+).*$/', "v$1", $tripal_version);
  111. $my_tripal_version = $tripal_version;
  112. if (array_key_exists('tv', $_GET)) {
  113. $tripal_version = $_GET['tv'];
  114. }
  115. if (array_key_exists('values', $form_state) and array_key_exists('tv', $form_state['values'])) {
  116. $tripal_version = $form_state['values']['tv'];
  117. }
  118. // Add the instructions.
  119. $form['instructions'] = array(
  120. '#type' => 'item',
  121. '#markup' => t('This page will help you find extensions that are available
  122. for Tripal. Select an extension type from the vertical tabs. The content
  123. of this page is constructed from an RSS feed provided by tripal.info.
  124. There may be no content if the tripal.info site is unavailable. The RSS
  125. feed will be cached for one hour.')
  126. );
  127. // Add the filters fieldset.
  128. $form['filters'] = array(
  129. '#type' => 'fieldset',
  130. '#title' => 'Filters',
  131. '#description' => t('You can filter which extensions are visible by
  132. changing the Tripal ahd Chado versions. By default only those
  133. extensions that are compatible with the currently installed Tripal
  134. and Chado verions are shown.'),
  135. '#collapsible' => TRUE,
  136. '#collapsed' => TRUE,
  137. );
  138. $summary_message = 'Currently showing extensions compatible with ';
  139. if ($tripal_version != 'any' and $chado_version != 'any') {
  140. $summary_message .= "<strong>Tripal $tripal_version</strong> and <strong>Chado $chado_version</strong>.";
  141. }
  142. elseif ($tripal_version == 'any' and $chado_version != 'any') {
  143. $summary_message .= "<strong>any Tripal</strong> version and <strong>Chado $chado_version</strong>.";
  144. }
  145. elseif ($tripal_version != 'any' and $chado_version == 'any') {
  146. $summary_message .= "<strong>Tripal $tripal_version</strong> and <strong>any Chado</strong> version.";
  147. }
  148. elseif ($tripal_version == 'any' and $chado_version == 'any') {
  149. $summary_message .= "<strong>any Tripal</strong> version and <strong>any Chado</strong> version.";
  150. }
  151. $form['filter_summary'] = array(
  152. '#type' => 'item',
  153. '#markup' => $summary_message,
  154. );
  155. // Add the Tripal version select box.
  156. $options = array();
  157. $options['any'] = '--Any--';
  158. foreach ($tvs as $tv) {
  159. $options[$tv] = $tv;
  160. }
  161. $form['filters']['tv'] = array(
  162. '#type' => 'select',
  163. '#title' => 'Tripal',
  164. '#options' => $options,
  165. '#default_value' => $tripal_version,
  166. '#ajax' => array(
  167. 'callback' => "tripal_extensions_form_ajax_callback",
  168. 'wrapper' => 'tripal_extensions',
  169. 'effect' => 'fade',
  170. 'method' => 'replace',
  171. ),
  172. '#prefix' => '<div style="float: left;">',
  173. '#suffix' => '</div>'
  174. );
  175. // Add the Chado version select box.
  176. $options = array();
  177. $options['any'] = '--Any--';
  178. foreach ($cvs as $cv) {
  179. $options[$cv] = $cv;
  180. }
  181. $form['filters']['cv'] = array(
  182. '#type' => 'select',
  183. '#title' => 'Chado',
  184. '#options' => $options,
  185. '#default_value' => $chado_version,
  186. '#ajax' => array(
  187. 'callback' => "tripal_extensions_form_ajax_callback",
  188. 'wrapper' => 'tripal_extensions',
  189. 'effect' => 'fade',
  190. 'method' => 'replace',
  191. ),
  192. '#prefix' => '<div style="float: left; padding-left: 10px">',
  193. '#suffix' => '</div>'
  194. );
  195. // Add the vertical tabs
  196. $form['extensions'] = array(
  197. '#type' => 'vertical_tabs',
  198. '#default_tab' => $tab,
  199. );
  200. // Add the fieldsets for each type
  201. foreach ($types as $type) {
  202. $form[$type] = array(
  203. '#id' => $type_ids[$type],
  204. '#type' => 'fieldset',
  205. '#title' => $type . 's',
  206. '#collapsed' => TRUE,
  207. '#collapsible' => TRUE,
  208. '#group' => 'extensions',
  209. );
  210. }
  211. // Iterate through all of the extensions and add them to the form.
  212. $compatible_extensions = array();
  213. if (array_key_exists($tripal_version, $extensions) and
  214. array_key_exists($chado_version, $extensions[$tripal_version])) {
  215. $compatible_extensions = $extensions[$tripal_version][$chado_version];
  216. }
  217. tripal_extension_form_add_extensions($form, $form_state,
  218. $compatible_extensions, $categories, $tripal_version,
  219. $chado_version, $my_tripal_version, $my_chado_version, $type_ids,
  220. $namespace, $filters);
  221. foreach ($types as $type) {
  222. if (count(element_children($form[$type])) == 0) {
  223. $form[$type]['empty'] = array(
  224. '#type' => 'item',
  225. '#markup' => '<strong>There are no matching ' . strtolower($type) . '(s).</strong>',
  226. );
  227. }
  228. }
  229. $form['#prefix'] = '<div id="tripal_extensions">';
  230. $form['#suffix'] = '</div>';
  231. $form['#submit'][] = 'tripal_extensions_form_submit';
  232. return $form;
  233. }
  234. /**
  235. * Adds each extension to the form.
  236. *
  237. * This function exits to simplify the the tripal_extension_form()
  238. * function.
  239. */
  240. function tripal_extension_form_add_extensions(&$form, $form_state, $extensions,
  241. $categories, $tripal_version, $chado_version, $my_tripal_version,
  242. $my_chado_version, $type_ids, $namespace, $filters) {
  243. // Iterate through the extensions. We will add a pager for
  244. // each type of extension, and display only those that should appear
  245. // on the page.
  246. $type_index = 0;
  247. foreach ($extensions as $type => $extensions) {
  248. $total_items = count($extensions);
  249. // Indicate how many matching extensions were found
  250. $form[$type]['total_found'] = array(
  251. '#type' => 'item',
  252. '#markup' => '<strong>Found ' . $total_items . ' matching ' . strtolower($type) . '(s)</strong>',
  253. );
  254. // Add the category select box;
  255. $cats = array_keys($categories[$tripal_version][$chado_version][$type]);
  256. sort($cats);
  257. $options = array();
  258. $options['any'] = '--Any--';
  259. foreach ($cats as $cat) {
  260. $options[$cat] = $cat;
  261. }
  262. // The default value can come from the pager links (thus via $_GET) or
  263. // via ajax call (thus via $form_state).
  264. $default_filter = '';
  265. if (array_key_exists('categories-' . $type_ids[$type], $_GET)) {
  266. $default_filter = $_GET['categories-' . $type_ids[$type]];
  267. }
  268. if (array_key_exists('values', $form_state) and array_key_exists('categories-' . $type_ids[$type], $form_state['values'])) {
  269. $default_filter = $form_state['values']['categories-' . $type_ids[$type]];
  270. }
  271. $form[$type]['filters']['categories-' . $type_ids[$type]] = array(
  272. '#type' => 'select',
  273. '#title' => 'Filter by Category',
  274. '#options' => $options,
  275. '#default_value' => $default_filter,
  276. '#ajax' => array(
  277. 'callback' => "tripal_extensions_form_ajax_callback",
  278. 'wrapper' => 'tripal_extensions',
  279. 'effect' => 'fade',
  280. 'method' => 'replace',
  281. ),
  282. );
  283. // Initialize pager and gets current page
  284. $num_per_page = 5;
  285. $page = pager_default_initialize($total_items, $num_per_page, $type_index);
  286. // Gets first record and last record to show
  287. $start = ($page) * $num_per_page;
  288. $end = ($start + $num_per_page < $total_items)? $start + $num_per_page : $total_items;
  289. // Iterate through each of the elements and add them to the form if
  290. // they are within the page
  291. $extension_index = 0;
  292. foreach ($extensions as $guid => $extension) {
  293. // Skip items that aren't on our current page.
  294. if ($extension_index < $start or $extension_index >= $end) {
  295. $extension_index++;
  296. continue;
  297. }
  298. $extension['title'] = trim($extension['title']);
  299. // If this is an extension module then there will be a home page for it
  300. $home_page = '';
  301. if (array_key_exists('home_page', $extension[$namespace])) {
  302. $home_page = "<strong>Project Home: </strong>" . $extension[$namespace]['home_page'] . "</br>";
  303. }
  304. // Determine if this extension is compatible with this site.
  305. $incompatible = '';
  306. $tvs_temp = preg_split('/, /', $extension[$namespace]['tripal_version']);
  307. $cvs_temp = preg_split('/, /', $extension[$namespace]['chado_version']);
  308. if (!in_array($my_tripal_version, $tvs_temp)) {
  309. $incompatible .= "<li>This extension is not compatible with this version of Tripal.</li>";
  310. }
  311. if (!in_array($my_chado_version, $cvs_temp)) {
  312. $incompatible .= "<li>This extension is not compatible with the installed Chado version.</li>";
  313. }
  314. $incompatible = t($incompatible);
  315. // Determine if this extension is already installed.
  316. $is_installed = '';
  317. switch ($type) {
  318. case 'Bulk Loader Template':
  319. if (module_exists('tripal_bulk_loader')) {
  320. $blk_id = db_select('tripal_bulk_loader_template' ,'tblt')
  321. ->fields('tblt', array('template_id'))
  322. ->condition('name', $extension['title'])
  323. ->execute()
  324. ->fetchField();
  325. if ($blk_id) {
  326. $is_installed = '<li>A bulk loader template with this name is already installed.</li>';
  327. }
  328. }
  329. break;
  330. case 'Materialized View':
  331. $mview_id = chado_get_mview_id($extension[$namespace]['mview_name']);
  332. if ($mview_id) {
  333. $is_installed = '<li>A materialized view with this name is already installed.</li>';
  334. }
  335. break;
  336. case 'Extension Module':
  337. if (array_key_exists('drupal_project', $extension[$namespace]) and
  338. module_exists($extension[$namespace]['drupal_project'])) {
  339. $is_installed = '<li>A module with this name is already installed.</li>';
  340. }
  341. break;
  342. default:
  343. break;
  344. }
  345. $is_installed = t($is_installed);
  346. // Does this module appear to be available on Drupal.org?
  347. $project = '';
  348. if ($type == 'Extension Module') {
  349. // Does it have a drupal project name?
  350. if (!array_key_exists('drupal_project', $extension[$namespace])) {
  351. // Since it doesn't have a drupal project name is it in a sandbox?
  352. if (!preg_match('/www.drupal.org\/sandbox/', $extension[$namespace]['home_page'])) {
  353. $project = t("<li>This module does not appear to be available as a " .
  354. "full Drupal project and thus cannot ".
  355. "be downloaded here. You may have to manually download it.</li>");
  356. }
  357. else {
  358. $project = ("<li>This module is in a sandbox on Drupal.org, and
  359. cannot be downloaded from here. You will have to manually download
  360. this module.</li>");
  361. }
  362. }
  363. }
  364. // Make sure the bulk loader module is installed, or we cannot provide
  365. // the bulk loader import button.
  366. $other = '';
  367. if ($type == 'Bulk Loader Template' and !module_exists('tripal_bulk_loader')) {
  368. $other = t('<li>The bulk loader
  369. module is not enabled. If you would like to import a loading template
  370. please enable it.</li>');
  371. }
  372. // If the user click's the button to import the extension then we
  373. // need the item in the submit function so we can process the import.
  374. $form[$type]['extension-' . $guid] = array(
  375. '#type' => 'value',
  376. '#value' => $extension,
  377. );
  378. $notices = '';
  379. if ($is_installed) {
  380. $notices = '<div class="messages status"><ul>' . $is_installed . '</ul></div>';
  381. }
  382. $warnings = '';
  383. if ($project or $other) {
  384. $warnings = '<div class="messages warning"><ul>' .
  385. $project . ' ' .
  386. $other . '</ul></div>';
  387. }
  388. $errors = '';
  389. if ($incompatible) {
  390. $errors = '<div class="messages error"><ul>' . $incompatible . '</ul></div>';
  391. }
  392. $state = '';
  393. if (array_key_exists('dev_stage', $extension[$namespace])) {
  394. $state = '<strong>Development State: </strong>' . $extension[$namespace]['dev_stage'] . "</br>";
  395. }
  396. // Create the form elements that we'll later theme into tables.
  397. $form[$type][$guid]['header'] = array(
  398. '#markup' => l($extension['title'], $extension['link']),
  399. );
  400. $form[$type][$guid]['details'] = array(
  401. '#markup' => "" .
  402. "<strong>Type:</strong> " . $type . "</br>" .
  403. "<strong>Categories: </strong>" . $extension[$namespace]['categories'] . "</br>" .
  404. "<strong>Authors: </strong>" . $extension[$namespace]['authors'] . "</br>" .
  405. $state .
  406. "<strong>Chado compatible versions: </strong>" . $extension[$namespace]['chado_version'] . "</br>" .
  407. "<strong>Tripal compatible versions: </strong>" . $extension[$namespace]['tripal_version'] . "</br>" .
  408. $home_page .
  409. "<strong>tripal.info Page: </strong>" . l($extension['link'], $extension['link']) . "</br>" .
  410. $notices .
  411. $warnings .
  412. $errors .
  413. "<p>" . $extension['description'] . "</p>",
  414. );
  415. // Add an import button to each of types that can support import.
  416. switch ($type) {
  417. case 'Bulk Loader Template':
  418. if (!$incompatible and !$is_installed and !$project) {
  419. $form[$type][$guid]['import'] = array(
  420. '#type' => 'submit',
  421. '#value' => "Import Loader",
  422. '#name' => "import-" . $guid,
  423. );
  424. }
  425. break;
  426. case 'Materialized View':
  427. if (!$incompatible and !$is_installed and !$project) {
  428. $form[$type][$guid]['import'] = array(
  429. '#type' => 'submit',
  430. '#value' => "Import MView",
  431. '#name' => "import-" . $guid,
  432. );
  433. }
  434. break;
  435. case 'Extension Module':
  436. if (!$incompatible and !$is_installed and !$project) {
  437. $form[$type][$guid]['import'] = array(
  438. '#type' => 'submit',
  439. '#value' => "Download Module",
  440. '#name' => "import-" . $guid,
  441. );
  442. }
  443. break;
  444. default:
  445. break;
  446. }
  447. $form[$type][$guid]['#theme'] = 'tripal_extensions_form_tables';
  448. $extension_index++;
  449. }
  450. // Now create and theme the pager.
  451. $pager = array(
  452. 'tags' => array(),
  453. 'element' => $type_index,
  454. 'parameters' => array(
  455. 'tab' => $type_ids[$type],
  456. 'cv' => $chado_version,
  457. 'tv' => $tripal_version,
  458. ),
  459. 'quantity' => $num_per_page,
  460. );
  461. // now add the category filters to the params array
  462. foreach ($filters as $filter_id => $value) {
  463. $pager['parameters']['categories-' . $filter_id] = $value;
  464. }
  465. // because this may be an ajax callback, the theme_pager will set the URL to be
  466. // "system/ajax", so we need to reset that
  467. $pager = theme('pager', $pager);
  468. global $base_path;
  469. $pager = str_replace($base_path . "system/ajax", "", $pager) ;
  470. $form[$type]['pager'] = array(
  471. '#type' => 'item',
  472. '#markup' => $pager,
  473. );
  474. $type_index++;
  475. }
  476. }
  477. /**
  478. * Process the import buttons.
  479. *
  480. * @param $form
  481. * @param $form_state
  482. */
  483. function tripal_extensions_form_submit($form, &$form_state) {
  484. // get the guid
  485. $clicked_button = $form_state['clicked_button']['#name'];
  486. $guid = preg_replace('/^import-(\d+)$/', "$1", $clicked_button);
  487. if ($guid) {
  488. $namespace = "http://tripal.info/rss/extensions/";
  489. $extension = $form_state['values']['extension-' . $guid];
  490. $type = $extension['category'];
  491. switch ($type) {
  492. case 'Bulk Loader Template':
  493. $options = array(
  494. 'template_name' => $extension['title'],
  495. 'template_array' => $extension[$namespace]['bulkldr_export'],
  496. 'strict' => TRUE,
  497. );
  498. $errors = array();
  499. $warnings = array();
  500. $success = tripal_insert_bulk_loader_template($options, $errors, $warnings);
  501. if ($success) {
  502. drupal_set_message("Bulk loader succesfully added.");
  503. }
  504. else {
  505. drupal_set_message("Error importing this bulk loader.", 'error');
  506. if (count($errors) > 0) {
  507. foreach($errors as $field => $message) {
  508. drupal_set_message($message, 'error');
  509. }
  510. }
  511. }
  512. break;
  513. case 'Materialized View':
  514. $module_name = 'tripal';
  515. $mview_name = $extension[$namespace]['mview_name'];
  516. $mview_schema = $extension[$namespace]['mview_schema'];
  517. $mview_sql = $extension[$namespace]['mview_sql'];
  518. // Validate the contents of the schema array.
  519. // TODO: security issue!! Before using 'eval' on this string
  520. // we should make sure the array is valid and there is no hidden
  521. // PHP code.
  522. $schema_array = array();
  523. $success = eval("\$schema_array = $mview_schema;");
  524. $error = chado_validate_custom_table_schema($schema_array);
  525. if ($error) {
  526. drupal_set_message("Cannot import Materialized View: $error", "error");
  527. }
  528. chado_add_mview($mview_name, $module_name, $schema_array, $mview_sql);
  529. break;
  530. case 'Extension Module':
  531. if (array_key_exists('drupal_project', $extension[$namespace])) {
  532. module_load_include('module', 'update', 'update');
  533. module_load_include('inc', 'update', 'update.manager');
  534. $project = $extension[$namespace]['drupal_project'];
  535. $tar = tripal_extensions_get_latest_module_version($project);
  536. if (!$tar) {
  537. drupal_set_message('Cannot find a suitable release of this module
  538. for download. You may need to manually install this module.', 'error');
  539. }
  540. else {
  541. // Download the file from the Drupal repository
  542. $local_cache = update_manager_file_get($tar);
  543. if (!$local_cache) {
  544. drupal_set_message('Cannot download the file. Check the
  545. "Recent log messages" for relavent errors.', 'error');
  546. }
  547. else {
  548. // The following code was borrowed from the update_manager_install_form_submit()
  549. // of drupal in the modules/update/update.manager.inc file.
  550. $directory = _update_manager_extract_directory();
  551. try {
  552. $archive = update_manager_archive_extract($local_cache, $directory);
  553. }
  554. catch (Exception $e) {
  555. drupal_set_message('Cannot extract the file. Please check
  556. permissions in the modules directory', 'error');
  557. return;
  558. }
  559. $archive_errors = update_manager_archive_verify($project, $local_cache, $directory);
  560. if (!empty($archive_errors)) {
  561. foreach ($archive_errors as $error) {
  562. drupal_set_message($error, 'error');
  563. }
  564. }
  565. $project_location = $directory . '/' . $project;
  566. try {
  567. $updater = Updater::factory($project_location);
  568. }
  569. catch (Exception $e) {
  570. drupal_set_message($e->getMessage(), 'error');
  571. return;
  572. }
  573. $project_real_location = drupal_realpath($project_location);
  574. $arguments = array(
  575. 'project' => $project,
  576. 'updater_name' => get_class($updater),
  577. 'local_url' => $project_real_location,
  578. );
  579. module_load_include('inc', 'update', 'update.authorize');
  580. $filetransfer = new FileTransferLocal(DRUPAL_ROOT);
  581. call_user_func_array('update_authorize_run_install', array_merge(array($filetransfer), $arguments));
  582. drupal_set_message('It appears the module was downloaded and
  583. extracted. You can now ' . l('enable this module'. 'admin/modules') . '.');
  584. }
  585. }
  586. }
  587. else {
  588. drupal_set_message('Cannot download this module. The Drpual project
  589. name is not set. You may have to manually download this module, and
  590. if possible encourage the developers to set the project name if it
  591. has been fully published on Drupal.org already.', 'error');
  592. }
  593. break;
  594. default:
  595. break;
  596. }
  597. }
  598. }
  599. /**
  600. * The theme function for rendering each element's table.
  601. *
  602. * @param $variables
  603. */
  604. function theme_tripal_extensions_form_tables($variables) {
  605. $element = $variables['element'];
  606. $headers = array(
  607. array(
  608. 'data' => drupal_render($element['header']),
  609. 'colspan' => 2,
  610. )
  611. );
  612. $button = array_key_exists('import', $element) ? drupal_render($element['import']) : '&nbsp;';
  613. $rows = array(
  614. array(
  615. array(
  616. 'data' => drupal_render($element['details']),
  617. ),
  618. array(
  619. 'data' => $button,
  620. 'width' => '5%',
  621. 'align' => 'right',
  622. ),
  623. ),
  624. );
  625. $table = array(
  626. 'header' => $headers,
  627. 'rows' => $rows,
  628. 'attributes' => array(),
  629. 'sticky' => FALSE,
  630. 'caption' => '',
  631. 'colgroups' => array(),
  632. 'empty' => '',
  633. );
  634. return theme_table($table);
  635. }
  636. /**
  637. * A callback function for the form
  638. *
  639. * @return unknown
  640. */
  641. function tripal_extensions_form_ajax_callback($form, $form_state) {
  642. // This function need not do anything as the form will take care of
  643. // updates needed. We just need to return the form.
  644. return $form;
  645. }
  646. /**
  647. * Determines the most recent downloadable package for a module.
  648. *
  649. * This function will connect to Drupal.org's RSS feed for a project and
  650. * determine the most recent version of the module and return the URL
  651. * for it's package.
  652. *
  653. * @param $project
  654. */
  655. function tripal_extensions_get_latest_module_version($project_name) {
  656. // First use the Drupal RESTful API to get the project
  657. $url = "https://www.drupal.org/api-d7/node.json?type=project_module&field_project_machine_name=$project_name";
  658. $result = json_decode(file_get_contents($url), TRUE);
  659. $project = $result['list'][0];
  660. $nid = $project['nid'];
  661. // Second get the releases for this node and find releases for this Drupal
  662. $url = "https://www.drupal.org/api-d7/node.json?type=project_release&field_release_project=$nid";
  663. $result = json_decode(file_get_contents($url), TRUE);
  664. $releases = $result['list'];
  665. $drupal_version = VERSION;
  666. $drupal_major = preg_replace('/^(\d+)\.\d+$/', "$1", $drupal_version);
  667. $best_release = NULL;
  668. foreach ($releases as $release) {
  669. // This release must match the Drupal major version or it won't
  670. // be compatiable. If it doesn't we'll skip it.
  671. $release_version = $release['field_release_version'];
  672. $release_drupal = preg_replace('/^(\d+)\.x-.*$/', "$1", $release_version);
  673. if ($release_drupal != $drupal_major) {
  674. continue;
  675. }
  676. // Set the current release to be the best one. On successive iterations
  677. // we'll check to see if that is still true.
  678. if ($best_release == NULL) {
  679. $best_release = $release;
  680. continue;
  681. }
  682. if ($release['field_release_version_major'] > $best_release['field_release_version_major']) {
  683. $best_release = $release;
  684. continue;
  685. }
  686. if ($release['field_release_version_patch'] > $best_release['field_release_version_patch']) {
  687. $best_release = $release;
  688. continue;
  689. }
  690. // If the best version has no extra part then let's keep it as this is the
  691. // most stable release.
  692. if (!$best_release['field_release_version_extra']) {
  693. continue;
  694. }
  695. // Convert the 'extra' part to a numeric value that will let us compare
  696. // the ranks of the release versions.
  697. $extra_rank = array(
  698. 'unstable' => 1,
  699. 'alpha' => 2,
  700. 'beta' => 3,
  701. 'rc' => 4,
  702. );
  703. $matches = array();
  704. $this_extra = 0;
  705. if (preg_match('/^(.*?)(\d+)$/', $release['field_release_version_extra'], $matches)) {
  706. $this_extra = $extra_rank[$matches[1]] . "." . $matches[2];
  707. }
  708. $best_extra = 0;
  709. if (preg_match('/^(.*?)(\d+)$/', $best_release['field_release_version_extra'], $matches)) {
  710. $best_extra = $extra_rank[$matches[1]] . "." . $matches[2];
  711. }
  712. if ($this_extra > $best_extra) {
  713. $best_release = $release;
  714. continue;
  715. }
  716. }
  717. // If we have a best result then build the download string.
  718. // TODO: we may need to make another web services call to get the actual
  719. // download path, but for now we'll hard code the construction of it.
  720. if ($best_release) {
  721. return 'http://ftp.drupal.org/files/projects/' . $project_name . '-' . $best_release['field_release_version'] . '.tar.gz';
  722. }
  723. return '';
  724. }