prod_monitor.admin.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. <?php
  2. /**
  3. * Build status page.
  4. */
  5. function prod_monitor_status($id) {
  6. $site = _prod_monitor_get_site($id, 'all');
  7. if (!$site) {
  8. // See https://api.drupal.org/api/drupal/includes!common.inc/function/drupal_not_found/7
  9. return MENU_NOT_FOUND;
  10. }
  11. drupal_set_title(t('Production monitor status for') . ' ' . _prod_monitor_sanitize_url($site['url']));
  12. $functions = $site['settings']['functions'];
  13. $nodata = t('No data recieved yet.');
  14. $output = '';
  15. // General status block
  16. $modules = _prod_monitor_get_site_modules($id);
  17. if(!empty($modules) && isset($site['data']['prod_mon'])) {
  18. $prod_mon = $site['data']['prod_mon'];
  19. $output .= _prod_monitor_status_general($prod_mon, $modules);
  20. }
  21. unset($site['data']['prod_mon']);
  22. // Performance data not needed here.
  23. unset($site['data']['perf_data']);
  24. // Display results of all checks.
  25. foreach ($functions as $set => $data) {
  26. if (isset($site['data'][$set])) {
  27. $output .= '<h2>' . t($data['title']) . '</h2>' . "\n";
  28. $output .= '<div class="description"><p><em>' . t($data['description']) . '</em></p></div>'."\n";
  29. if (!empty($site['data'][$set])) {
  30. $output .= theme('prod_monitor_status_report', array('requirements' => $site['data'][$set]));
  31. }
  32. else {
  33. $output .= '<p>' . $nodata . '</p><p>&nbsp;</p>';
  34. }
  35. }
  36. }
  37. if (empty($output)) {
  38. $output = '<p>' . $nodata . '</p><p>&nbsp;</p>';
  39. }
  40. // TODO: do not use drupal_render but change this so that hook_page_alter can
  41. // be used as well.
  42. $form = drupal_get_form('_prod_monitor_update_data_form', $id, $site);
  43. $output .= drupal_render($form);
  44. return $output;
  45. }
  46. /**
  47. * Helper function to provide general status block on status overview page
  48. */
  49. function _prod_monitor_status_general($prod_mon, $modules) {
  50. $updates = _prod_monitor_generate_updates_link($modules['id'], $modules['updates']);
  51. $output = '<h2>' . t('Overall status') . '</h2>' . "\n";
  52. $rows = array(
  53. array(
  54. array('data' => t('Drupal core version'), 'header' => TRUE),
  55. $modules['projects']['drupal']['info']['version'],
  56. ),
  57. );
  58. if (isset($prod_mon['prod_check_cron_last'])) {
  59. $rows[] = array(
  60. array('data' => t('Last cron run'), 'header' => TRUE),
  61. format_date($prod_mon['prod_check_cron_last'], 'large'),
  62. );
  63. }
  64. // Add dbconnect check info if configured.
  65. if (isset($prod_mon['prod_check_dbconnect'])) {
  66. $dbconnect = $prod_mon['prod_check_dbconnect'];
  67. $class = array();
  68. $title = t('DB connection status');
  69. if (stripos($dbconnect, '200') === FALSE) {
  70. $class = array('error');
  71. $title = '<strong>' . $title . '</strong>';
  72. $dbconnect = '<strong>' . $dbconnect . '</strong>';
  73. }
  74. $rows[] = array(
  75. 'data' => array(
  76. array(
  77. 'data' => $title, 'header' => TRUE,
  78. 'class' => $class,
  79. ),
  80. array(
  81. 'data' => $dbconnect,
  82. 'class' => $class,
  83. ),
  84. ),
  85. 'class' => $class,
  86. );
  87. }
  88. $rows[] = array(
  89. array('data' => t('Update status'), 'header' => TRUE),
  90. $updates,
  91. );
  92. $output .= theme('table', array('prod_monitor_id' => 'status_general', 'header' => array(), 'rows' => $rows));
  93. return $output;
  94. }
  95. /**
  96. * Helper function to generate update status link for tables
  97. */
  98. function _prod_monitor_generate_updates_link($id, $update_status) {
  99. $updates = t('Unknown');
  100. if ($update_status > 0) {
  101. switch ($update_status) {
  102. case 1:
  103. $class = '';
  104. $title = t('None');
  105. break;
  106. case 2:
  107. $class = 'warning';
  108. $title = t('Available');
  109. break;
  110. case 3:
  111. $class = 'error';
  112. $title = t('Security risk!');
  113. break;
  114. }
  115. $updates = array('data' => '<strong>' . l($title, 'admin/reports/prod-monitor/site/' . $id . '/view/updates', array('attributes' => array('title' => $title, 'class' => $class))) . '</strong>', 'class' => $class);
  116. }
  117. return $updates;
  118. }
  119. /**
  120. * Callback for performance page.
  121. */
  122. function prod_monitor_performance($data) {
  123. drupal_set_title(t('Performance logs for') . ' ' . _prod_monitor_get_url($data['id']));
  124. // TODO: add 'get stats now' button.
  125. //$site = _prod_monitor_get_site($id, 'all');
  126. return array(
  127. 'performance_data' => array(
  128. '#data' => $data['data'],
  129. '#theme' => 'prod_monitor_performance',
  130. ),
  131. );
  132. }
  133. /**
  134. * Callback for module update status page
  135. */
  136. function prod_monitor_updates($modules) {
  137. $output = '';
  138. $id = $modules['id'];
  139. drupal_set_title(t('Module update status for') . ' ' . _prod_monitor_get_url($id));
  140. // Only show a report if the available updates have been fetched!
  141. if (!empty($modules) && !empty($modules['projects']) && !empty($modules['available'])) {
  142. module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
  143. $data = _prod_monitor_calculate_project_data($id, $modules['projects'], $modules['available']);
  144. $output .= theme('prod_monitor_update_report', array('id' => $id, 'last' => $modules['lastupdate'], 'data' => $data));
  145. }
  146. // No data, so report this to the user and instruct him/her on how to retrieve it.
  147. else {
  148. $destination = drupal_get_destination();
  149. $output .= theme('prod_monitor_update_report',
  150. array(
  151. 'id' => $id,
  152. 'last' => $modules['lastupdate'],
  153. 'data' => t(
  154. 'No information is available about potential new releases for currently installed modules and themes. To check for updates, you may need to !cron or you can !check. Please note that checking for available updates can take a long time, so please be patient.',
  155. array(
  156. '!cron' => l(t('run cron'), 'admin/reports/status/run-cron', array('attributes' => array('title' => t('run cron')), 'query' => $destination)),
  157. '!check' => l(t('check manually'), 'admin/reports/prod-monitor/site/' . $id . '/update-check', array('attributes' => array('title' => t('check manually')))),
  158. )
  159. )
  160. )
  161. );
  162. }
  163. return $output;
  164. }
  165. /**
  166. * Callback to refresh the module update status page
  167. */
  168. function prod_monitor_updates_check($id) {
  169. // Get module data.
  170. $modules = _prod_monitor_get_site_modules($id);
  171. if (!empty($modules) && !empty($modules['projects'])) {
  172. module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
  173. // ALWAYS do a full refresh.
  174. $result = _prod_monitor_update_refresh($id, $modules['projects'], $modules['sitekey']);
  175. if (!empty($result)) {
  176. drupal_set_message(t('Information about all available new releases and updates sucessfully fetched.'));
  177. }
  178. else {
  179. drupal_set_message(t('Failed to fetch all available new releases and updates!'), 'error');
  180. }
  181. }
  182. else {
  183. drupal_set_message(t('No module data available: cannot check for updates!'), 'error');
  184. }
  185. drupal_goto('admin/reports/prod-monitor/site/' . $id . '/view/updates');
  186. }
  187. /**
  188. * Build settings form.
  189. */
  190. function prod_monitor_overview_form($form, &$form_state, $edit = FALSE) {
  191. drupal_set_title(t('Production monitor settings'));
  192. $base = drupal_get_path('module', 'prod_monitor');
  193. drupal_add_css($base . '/css/prod-monitor.css', 'file');
  194. drupal_add_js($base . '/js/jquery.equalheights.js', 'file');
  195. drupal_add_js($base . '/js/prod-monitor.js', 'file');
  196. $form = array();
  197. $collapsed = FALSE;
  198. if (!$edit) {
  199. // Button to initiate our fetch all batcher.
  200. $form['fetch_all_submit'] = array(
  201. '#weight' => -100,
  202. '#type' => 'submit',
  203. '#value' => t('Fetch all'),
  204. '#submit' => array('prod_monitor_fetch_all_submit'),
  205. '#limit_validation_errors' => array(),
  206. );
  207. // Add new site situation.
  208. $sites = _prod_monitor_get_sites();
  209. $dbconnect_path = $api_key = $url = '';
  210. $options = array();
  211. $button = t('Get settings');
  212. if (!empty($sites)) {
  213. $form['table'] = array(
  214. '#markup' => _prod_monitor_overview_form_table($sites),
  215. );
  216. $collapsed = TRUE;
  217. }
  218. if (!empty($form_state['storage']['get_settings'])) {
  219. // Second step of add new site situation.
  220. $api_key = $form_state['values']['api_key'];
  221. $dbconnect_path = $form_state['values']['dbconnect_path'];
  222. $url = $form_state['values']['url'];
  223. $button = t('Add site');
  224. $collapsed = FALSE;
  225. $msg = TRUE;
  226. }
  227. }
  228. else {
  229. // Edit site situation.
  230. $site = _prod_monitor_get_site($edit);
  231. $url = $site['url'];
  232. // Allow user to correct faulty url.
  233. if (isset($form_state['values']['url']) && $url != $form_state['values']['url']) {
  234. $url = $form_state['values']['url'];
  235. }
  236. drupal_set_title(t('Production monitor settings for !url', array('!url' => _prod_monitor_sanitize_url($url))));
  237. $api_key = $site['settings']['api_key'];
  238. $dbconnect_path = $site['settings']['dbconnect_path'];
  239. $options = $site['settings']['checks'];
  240. if (isset($site['settings']['checks']['perf_data'])) {
  241. $perf_enabled = $site['settings']['checks']['perf_data'];
  242. }
  243. $button = t('Save site');
  244. $msg = FALSE;
  245. }
  246. // Add/edit form.
  247. $form['sites'] = array(
  248. '#type' => 'fieldset',
  249. '#title' => t('Add a site to monitor'),
  250. '#description' => t('You can add sites here that you wish to monitor.'),
  251. '#collapsible' => TRUE,
  252. '#collapsed' => $collapsed,
  253. );
  254. $form['sites']['url'] = array(
  255. '#type' => 'textfield',
  256. '#title' => t('Website URL'),
  257. '#default_value' => $url,
  258. '#description' => t('Enter the <strong>full</strong> url of the website to be monitored. This site must be running the <em>Production check</em> module. This <strong>must</strong> include a protocol like <em>http://</em> or <em>https://</em>!'),
  259. '#size' => 60,
  260. '#required' => TRUE,
  261. );
  262. $form['sites']['api_key'] = array(
  263. '#type' => 'textfield',
  264. '#title' => t("The website's API key"),
  265. '#default_value' => $api_key,
  266. '#description' => t('Enter the API key you have configured for this site using the <em>Production check</em> module.'),
  267. '#size' => 60,
  268. '#maxlength' => 128,
  269. '#required' => TRUE,
  270. );
  271. $form['sites']['dbconnect_path'] = array(
  272. '#type' => 'textfield',
  273. '#title' => t('DB connect check script'),
  274. '#default_value' => $dbconnect_path,
  275. '#description' => t('Enter the relative path, starting from the Drupal root, to the <em>prod_check.dbconnect.php</em> on the remote site. This wil usually be something like sites/all/modules/contrib/prod_check/prod_check.dbconnect.php . <strong>Leave empty if you do not want do enable this check!</strong>'),
  276. '#size' => 60,
  277. '#maxlength' => 512,
  278. );
  279. // Only show on second step of add form or when editing.
  280. if (!empty($form_state['storage']['get_settings']) || $edit) {
  281. // Get the settings from the remote site. We always do this when the form is
  282. // displayed and don't store this locally. Logic here is that you won't be
  283. // editing these settings all that much.
  284. $functions = _prod_monitor_retrieve_functions($url, $api_key, $msg);
  285. // Parse the array of functions into a form.
  286. if ($functions) {
  287. // Save data to store in DB on submit.
  288. $form_state['storage']['functions'] = $functions;
  289. // Parse functions into form data
  290. $form['sites']['prod_check_settings'] = array(
  291. '#type' => 'fieldset',
  292. '#title' => t('Configure what data you wish to monitor for this site.'),
  293. '#collapsible' => TRUE,
  294. '#collapsed' => FALSE,
  295. );
  296. $form['sites']['prod_check_settings']['monitor_settings'] = array(
  297. '#type' => 'markup',
  298. '#prefix' => '<div id="prod-check-settings">',
  299. '#suffix' => '</div>',
  300. '#tree' => TRUE,
  301. );
  302. // Single out perf_data if present to treat differently.
  303. if (isset($functions['perf_data'])) {
  304. $performance = $functions['perf_data'];
  305. unset($functions['perf_data']);
  306. }
  307. $i = 1;
  308. foreach ($functions as $set => $data) {
  309. $rest = $i%2;
  310. $form['sites']['prod_check_settings']['monitor_settings'][$set] = array(
  311. '#type' => 'checkboxes',
  312. '#title' => t($data['title']),
  313. '#description' => t($data['description']),
  314. '#options' => $data['functions'],
  315. '#default_value' => array_keys($data['functions']),
  316. '#prefix' => '<div class="prod-check-settings ' . (($rest) ? 'odd' : 'even') . '">',
  317. '#suffix' => '</div>',
  318. );
  319. $i++;
  320. }
  321. if ($edit) {
  322. // Just to increase readability of the source code here.
  323. $monitor_settings = &$form['sites']['prod_check_settings']['monitor_settings'];
  324. // Set default values to last saved state
  325. foreach (element_children($monitor_settings) as $set) {
  326. if (isset($options[$set])) {
  327. $monitor_settings[$set]['#default_value'] = $options[$set];
  328. }
  329. else {
  330. // No settings available, so uncheck all.
  331. $monitor_settings[$set]['#default_value'] = array();
  332. }
  333. }
  334. }
  335. // Add performance logging section.
  336. if (!empty($performance['functions'])) {
  337. $form['sites']['prod_check_performance'] = array(
  338. '#type' => 'fieldset',
  339. '#title' => t("Configure which module's performance data you wish to log."),
  340. '#collapsible' => TRUE,
  341. '#collapsed' => FALSE,
  342. );
  343. $perf_options = array();
  344. foreach ($performance['functions'] as $function => $title) {
  345. $perf_options[$function] = t($title);
  346. }
  347. $form['sites']['prod_check_performance']['performance'] = array(
  348. '#type' => 'checkboxes',
  349. '#options' => $perf_options,
  350. '#default_value' => array(),
  351. '#description' => t('Indicate which performance data you want to store. This data can be provided by the modules listed here.'),
  352. );
  353. if ($edit && isset($perf_enabled)) {
  354. $form['sites']['prod_check_performance']['performance']['#default_value'] = $perf_enabled;
  355. }
  356. }
  357. $form['sites']['fetch'] = array(
  358. '#type' => 'checkbox',
  359. '#title' => t('Fetch data immediately'),
  360. '#default_value' => 0,
  361. '#description' => t('Will attempt to fetch the data immediately when the site has been added.'),
  362. );
  363. }
  364. else {
  365. // Error, so show retry button.
  366. $button = t('Retry');
  367. }
  368. }
  369. if ($edit) {
  370. $form['site_id'] = array(
  371. '#type' => 'value',
  372. '#value' => $edit,
  373. );
  374. }
  375. $form['sites']['submit'] = array(
  376. '#type' => 'submit',
  377. '#value' => $button,
  378. );
  379. return $form;
  380. }
  381. /**
  382. * Helper function to theme all sites into a table
  383. */
  384. function _prod_monitor_overview_form_table($sites) {
  385. $home = array('destination' => 'admin/reports/prod-monitor');
  386. // Set headers.
  387. $headers = array(
  388. t('URL'),
  389. t('Data'),
  390. t('Status'),
  391. t('Updates'),
  392. t('Date added'),
  393. t('Last update'),
  394. array('data' => t('Actions'), 'colspan' => 5),
  395. );
  396. // Compose rows.
  397. $rows = array();
  398. foreach ($sites as $id => $site_info) {
  399. // Set view and flush links.
  400. $view = t('View');
  401. $flush = t('Flush');
  402. if ($site_info['data']) {
  403. $view = l(t('View'), 'admin/reports/prod-monitor/site/' . $id, array('attributes' => array('title' => t('View'))));
  404. $flush = l(t('Flush'), 'admin/reports/prod-monitor/site/' . $id . '/flush', array('attributes' => array('title' => t('Flush'))));
  405. }
  406. $update_status = _prod_monitor_get_update_status($id);
  407. $updates = _prod_monitor_generate_updates_link($id, $update_status);
  408. if (!empty($site_info['status'])) {
  409. $title = t(ucwords($site_info['status']));
  410. $status = array('data' => '<strong>' . l($title, 'admin/reports/prod-monitor/site/' . $id, array('attributes' => array('title' => $title, 'class' => $site_info['status']))) . '</strong>', 'class' => array($site_info['status']));
  411. }
  412. else {
  413. $status = '';
  414. }
  415. // Actually compose the rows.
  416. $row = array(
  417. 'data' => array(
  418. _prod_monitor_sanitize_url($site_info['url']),
  419. (!$site_info['data']) ? t('Not yet retrieved.') : t('Stored.'),
  420. $status,
  421. $updates,
  422. $site_info['added'],
  423. (!$site_info['lastupdate']) ? t('Not yet updated.') : $site_info['lastupdate'],
  424. /* Compose links. */
  425. $view,
  426. l(t('Edit'), 'admin/reports/prod-monitor/site/' . $id . '/edit', array('query' => $home, 'attributes' => array('title' => t('Edit')))),
  427. l(t('Fetch data'), 'admin/reports/prod-monitor/site/' . $id . '/fetch', array('attributes' => array('title' => t('Fetch & View')))),
  428. $flush,
  429. l(t('Delete'), 'admin/reports/prod-monitor/site/' . $id . '/delete', array('attributes' => array('title' => t('Delete')))),
  430. ),
  431. 'class' => array($site_info['status']),
  432. );
  433. $rows[] = $row;
  434. }
  435. return theme('table', array('prod_monitor_id' => 'overview_form', 'header' => $headers, 'rows' => $rows));
  436. }
  437. /**
  438. * Validation function
  439. */
  440. function prod_monitor_overview_form_validate($form, &$form_state) {
  441. if (!preg_match('/^https?:\/\/.*/i', $form_state['values']['url'])) {
  442. form_set_error('url', t('The url must start with a valid protocol: either http:// or https://'));
  443. }
  444. }
  445. /**
  446. * Submit function
  447. */
  448. function prod_monitor_overview_form_submit($form, &$form_state) {
  449. switch ($form_state['values']['op']) {
  450. case t('Get settings'):
  451. case t('Retry'):
  452. // Make sure the storage is not empty so we go to step 2
  453. $form_state['storage']['get_settings'] = TRUE;
  454. $form_state['rebuild'] = TRUE;
  455. break;
  456. case t('Add site'):
  457. case t('Save site'):
  458. // Prevent from ending on step 2 again.
  459. unset($form_state['storage']['get_settings']);
  460. $site = new stdClass();
  461. // Edit situation, so force an update.
  462. if (isset($form_state['values']['site_id']) && is_numeric($form_state['values']['site_id'])) {
  463. $update = array('id');
  464. $site->id = $form_state['values']['site_id'];
  465. }
  466. else {
  467. // Add situation, insert.
  468. $update = array();
  469. $site->added = time();
  470. }
  471. $site->url = $form_state['values']['url'];
  472. // Get enabled checks.
  473. $checks = array();
  474. foreach ($form_state['values']['monitor_settings'] as $set => $data) {
  475. foreach ($data as $check => $value) {
  476. if ($value) {
  477. $checks[$set][] = $value;
  478. }
  479. }
  480. }
  481. // Get enabled performance logs.
  482. if (!empty($form_state['values']['performance'])) {
  483. $checks['perf_data'] = array();
  484. foreach ($form_state['values']['performance'] as $value) {
  485. if ($value) {
  486. $checks['perf_data'][] = $value;
  487. }
  488. }
  489. }
  490. // Prepare settings data.
  491. $site->settings = serialize(
  492. array(
  493. 'api_key' => $form_state['values']['api_key'],
  494. // Trim spaces and / from left and right side.
  495. 'dbconnect_path' => trim($form_state['values']['dbconnect_path'], ' /'),
  496. 'functions' => $form_state['storage']['functions'],
  497. 'checks' => $checks,
  498. )
  499. );
  500. $result = drupal_write_record('prod_monitor_sites', $site, $update);
  501. if ($result) {
  502. drupal_set_message(t('Website %url correctly saved.', array('%url' => $site->url)));
  503. if ($form_state['values']['fetch']) {
  504. $site_info = _prod_monitor_get_site($site->id, 'all');
  505. // First: all checks.
  506. _prod_monitor_retrieve_data($site->id, $site_info, TRUE);
  507. // MUST be second because of the status update!
  508. _prod_monitor_db_connect_check($site->id, $site_info);
  509. $form_state['redirect'] = 'admin/reports/prod-monitor/site/' . $site->id;
  510. }
  511. }
  512. else {
  513. drupal_set_message(t('Website %url not saved! Please try again.', array('%url' => $site->url)), 'error');
  514. }
  515. break;
  516. }
  517. }
  518. /**
  519. * Submit handler for the fetch all button.
  520. */
  521. function prod_monitor_fetch_all_submit($form, &$form_state) {
  522. _prod_monitor_fetch_all_data_batcher_create();
  523. }
  524. /**
  525. * Function to create the batch process. Also used in Drush!
  526. */
  527. function _prod_monitor_fetch_all_data_batcher_create($fetch_only = FALSE, $update_only = FALSE, $msg = TRUE) {
  528. $title = t('Fetching all site data and checking for updates...');
  529. if ($fetch_only) {
  530. $title = t('Fetching all site data...');
  531. }
  532. if ($update_only) {
  533. $title = t('Checking for updates...');
  534. }
  535. $batch = array(
  536. 'operations' => array(
  537. array('prod_monitor_fetch_all_data_batcher', array($fetch_only, $update_only, $msg)),
  538. ),
  539. 'title' => $title,
  540. 'file' => drupal_get_path('module', 'prod_monitor') . '/includes/prod_monitor.admin.inc',
  541. );
  542. batch_set($batch);
  543. }
  544. /**
  545. * Batch fetching of all site info.
  546. */
  547. function prod_monitor_fetch_all_data_batcher($fetch_only, $update_only, $msg, &$context) {
  548. $sandbox = &$context['sandbox'];
  549. if (empty($context['sandbox'])) {
  550. $sandbox['sites'] = array_keys(_prod_monitor_get_sites());
  551. $sandbox['count'] = count($sandbox['sites']);
  552. }
  553. // Get site info.
  554. $id = array_shift($sandbox['sites']);
  555. $site_info = _prod_monitor_get_site($id, 'all');
  556. if (!$update_only) {
  557. // First: all checks.
  558. _prod_monitor_retrieve_data($id, $site_info, $msg);
  559. // MUST be second because of the status update!
  560. _prod_monitor_db_connect_check($id, $site_info);
  561. }
  562. if (!$fetch_only) {
  563. // Module update status.
  564. $modules = prod_monitor_load($id);
  565. if (!empty($modules) && !empty($modules['projects'])) {
  566. module_load_include('inc', 'prod_monitor', 'includes/prod_monitor.update');
  567. _prod_monitor_update_refresh($id, $modules['projects'], $modules['sitekey']);
  568. _prod_monitor_calculate_project_data($id, $modules['projects'], $modules['available']);
  569. }
  570. }
  571. $context['message'] = t('Updating data for %url', array('%url' => $site_info['url']));
  572. $context['finished'] = empty($sandbox['sites']) ? 1 : (1 - count($sandbox['sites']) / $sandbox['count']);
  573. }
  574. /**
  575. * Callback to fetch site data
  576. */
  577. function prod_monitor_fetch_data($id) {
  578. $site_info = _prod_monitor_get_site($id, 'all');
  579. // First: all checks.
  580. _prod_monitor_retrieve_data($id, $site_info, TRUE);
  581. // MUST be second because of the status update!
  582. _prod_monitor_db_connect_check($id, $site_info);
  583. drupal_goto('admin/reports/prod-monitor/site/' . $id);
  584. }
  585. /**
  586. * Form to delete a site's data
  587. */
  588. function prod_monitor_flush_form($form, &$form_state, $id) {
  589. $form = array();
  590. $form['site_id'] = array(
  591. '#type' => 'value',
  592. '#value' => $id,
  593. );
  594. $url = _prod_monitor_get_url($id);
  595. $form['url'] = array(
  596. '#type' => 'value',
  597. '#value' => $url,
  598. );
  599. return confirm_form($form, t('Are you sure you wish to delete all fetched data for %url?', array('%url' => $url)), 'admin/reports/prod-monitor', t('Note that the module update status data will not be flushed!') . '<br />' . t('This action cannot be undone.'));
  600. }
  601. /**
  602. * Delete a site's data
  603. */
  604. function prod_monitor_flush_form_submit($form, &$form_state) {
  605. if ($form_state['values']['site_id']) {
  606. $result = _prod_monitor_flush_data($form_state['values']['site_id']);
  607. if ($result === FALSE) {
  608. drupal_set_message(t('Unable to flush data for %url!', array('%url' => $form_state['values']['url'])), 'error');
  609. }
  610. else {
  611. drupal_set_message(t('Stored data for %url successfully flushed.', array('%url' => $form_state['values']['url'])));
  612. }
  613. }
  614. $form_state['redirect'] = 'admin/reports/prod-monitor';
  615. }
  616. /**
  617. * Form to delete a site
  618. */
  619. function prod_monitor_delete_form($form, &$form_state, $id) {
  620. $form = array();
  621. $form['site_id'] = array(
  622. '#type' => 'value',
  623. '#value' => $id,
  624. );
  625. $url = _prod_monitor_get_url($id);
  626. $form['url'] = array(
  627. '#type' => 'value',
  628. '#value' => $url,
  629. );
  630. return confirm_form($form, t('Are you sure you wish to delete the website %url?', array('%url' => $url)), 'admin/reports/prod-monitor');
  631. }
  632. /**
  633. * Delete a site
  634. */
  635. function prod_monitor_delete_form_submit($form, &$form_state) {
  636. if ($form_state['values']['site_id']) {
  637. $result = _prod_monitor_delete_site($form_state['values']['site_id']);
  638. if ($result) {
  639. drupal_set_message(t('Website %url successfully deleted.', array('%url' => $form_state['values']['url'])));
  640. }
  641. else {
  642. drupal_set_message(t('Unable to delete %url!', array('%url' => $form_state['values']['url'])), 'error');
  643. }
  644. }
  645. $form_state['redirect'] = 'admin/reports/prod-monitor';
  646. }
  647. function _prod_monitor_update_data_form($form, $form_state, $id, $site_info) {
  648. $form['site_id'] = array(
  649. '#type' => 'value',
  650. '#value' => $id,
  651. );
  652. $form['site_info'] = array(
  653. '#type' => 'value',
  654. '#value' => $site_info,
  655. );
  656. // Markup field for proper styling.
  657. $form['buttons'] = array(
  658. '#type' => 'markup',
  659. '#prefix' => '<div class="buttons">',
  660. '#value' => '&nbsp;',
  661. '#suffix' => '</div>',
  662. );
  663. $form['buttons']['submit'] = array(
  664. '#type' => 'submit',
  665. '#value' => t('Fetch data now'),
  666. );
  667. return $form;
  668. }
  669. function _prod_monitor_update_data_form_submit($form, &$form_state) {
  670. // First: all checks.
  671. _prod_monitor_retrieve_data($form_state['values']['site_id'], $form_state['values']['site_info'], TRUE);
  672. // MUST be second because of the status update!
  673. _prod_monitor_db_connect_check($form_state['values']['site_id'], $form_state['values']['site_info']);
  674. }
  675. /**
  676. * Callback for module lookup form.
  677. *
  678. */
  679. function prod_monitor_module_lookup_form($form, &$form_state) {
  680. $form = array();
  681. // Show results.
  682. if (isset($form_state['projects'])) {
  683. $module = $form_state['values']['module'];
  684. $rows = array();
  685. foreach ($form_state['projects'] as $id => $project) {
  686. $application = db_select('prod_monitor_sites', 'pms')->fields('pms', array('url'))->condition('id', $id)->execute()->fetchField();
  687. $version = $project['info']['version'];
  688. $rows[] = array(
  689. $application,
  690. $version,
  691. l(t('View'), 'admin/reports/prod-monitor/site/' . $id),
  692. );
  693. }
  694. $form['title'] = array(
  695. '#theme' => 'html_tag',
  696. '#tag' => 'h2',
  697. '#value' => t('Applications where %module is used', array('%module' => $module)),
  698. );
  699. $form['table'] = array(
  700. '#theme' => 'table',
  701. '#rows' => $rows,
  702. '#header' => array(t('Application'), t('Version'), t('View')),
  703. );
  704. $form['submit'] = array(
  705. '#type' => 'submit',
  706. '#value' => t('Perform another lookup'),
  707. );
  708. }
  709. // Show lookup form.
  710. else {
  711. $form['module'] = array(
  712. '#type' => 'textfield',
  713. '#title' => t('Module name'),
  714. '#required' => TRUE,
  715. );
  716. $form['submit'] = array(
  717. '#type' => 'submit',
  718. '#value' => t('Fetch Applications'),
  719. );
  720. }
  721. return $form;
  722. }
  723. /**
  724. * Submission handler for module lookup form
  725. */
  726. function prod_monitor_module_lookup_form_submit(&$form, &$form_state) {
  727. if (isset($form_state['values']['module'])) {
  728. $projects = db_select('prod_monitor_site_modules', 'psm')
  729. ->fields('psm', array('id', 'projects'))
  730. ->condition('projects', '%' . db_like($form_state['values']['module']) . '%', 'LIKE')
  731. ->execute()->fetchAllAssoc('id');
  732. $form_state['rebuild'] = TRUE;
  733. if ($projects && !empty($projects)) {
  734. foreach($projects as $project) {
  735. $modules = unserialize($project->projects);
  736. if (isset($modules[$form_state['values']['module']])) {
  737. $form_state['projects'][$project->id] = $modules[$form_state['values']['module']];
  738. }
  739. }
  740. }
  741. if (!isset($form_state['projects'])) {
  742. drupal_set_message(t('No projects found for :module', array(':module' => $form_state['values']['module'])));
  743. }
  744. }
  745. else {
  746. unset($form_state['projects']);
  747. }
  748. }