diff --git a/acquia_agent/acquia_agent.module b/acquia_agent/acquia_agent.module
index eb865af..d25e640 100644
--- a/acquia_agent/acquia_agent.module
+++ b/acquia_agent/acquia_agent.module
@@ -529,6 +529,8 @@ function acquia_agent_help($path, $arg) {
$output .= '
' . t('Enables secure communication between your Drupal sites and the Acquia Network.') . '';
$output .= 'Acquia SPI';
$output .= '' . t('Automates the collection of site information. Required for use with the Acquia Insight service.') . '';
+ $output .= 'Acquia SPI Custom Tests';
+ $output .= '' . t('Acquia Insight supports custom tests for your site. See acquia_spi.api.php for information on the custom test hook and validate your tests for inclusion in outgoing SPI data with the Drush command, spi-test-validate.') . '';
$output .= 'Acquia Search';
$output .= '' . t('Provides authentication service to the Apache Solr Search Integration module to enable use of Acquia\'s hosted Solr search indexes.') . '';
$output .= '';
diff --git a/acquia_spi/acquia_spi.api.php b/acquia_spi/acquia_spi.api.php
index bae4a6b..20e7ace 100644
--- a/acquia_spi/acquia_spi.api.php
+++ b/acquia_spi/acquia_spi.api.php
@@ -33,3 +33,42 @@ function hook_acquia_spi_get() {
);
return $data;
}
+
+/**
+ * Include data to be sent to Acquia Insight as part of the SPI process. This
+ * data will be stored on Acquia's servers in an unencrypted database, so be
+ * careful not to send sensitive information in any field. Multiple tests can
+ * also be added per callback provided that each test has a unique identifier.
+ *
+ * @return
+ * An array of user-contributed test data keyed by unique identifier.
+ * - (string) description: Detailed information regarding test, its impact,
+ * and other relevant details. Cannot exceed 1024 characters.
+ * - (string) solved_message: The message to display when the test has
+ * succeeded. Cannot exceed 1024 characters.
+ * - (string) failed_message: The message to display when the test has failed.
+ * Cannot exceed 1024 characters.
+ * - (boolean) solved: A flag indicating whether or not the test was
+ * successful.
+ * - (string) fix_details: Information on how to fix or resolve the test if
+ * failed. Cannot exceed 1024 characters.
+ * - (string) category: The category to place the test within. Must be either
+ * 'performance', 'security, or 'best_practices'.
+ * - (int) severity: The priority level of the custom test. Must be either
+ * 0, 1, 2, 4, 8, 16, 32, 64, or 128. Higher severities impact
+ * the Insight score proportionally.
+ *
+ */
+function hook_acquia_spi_test() {
+ return array(
+ 'unique_example' => array(
+ 'description' => 'This example test is useful.',
+ 'solved_message' => 'The test was successful',
+ 'failed_message' => 'The test has failed',
+ 'solved' => TRUE,
+ 'fix_details' => 'Please resolve this issue using this fix information.',
+ 'category' => 'best_practices',
+ 'severity' => 0,
+ ),
+ );
+}
diff --git a/acquia_spi/acquia_spi.drush.inc b/acquia_spi/acquia_spi.drush.inc
index 7d6690c..06eefb5 100644
--- a/acquia_spi/acquia_spi.drush.inc
+++ b/acquia_spi/acquia_spi.drush.inc
@@ -19,6 +19,11 @@ function acquia_spi_drush_command() {
'spi-get --format=json --outfile=spi.json' => 'Write JSON encoded SPI data to spi.json in current directory.',
),
);
+ $items['spi-test-validate'] = array(
+ 'callback' => 'drush_acquia_spi_custom_test_validate',
+ 'description' => dt('Perform a validation check on any modules with Acquia SPI custom tests.'),
+ 'aliases' => array('spi-tv'),
+ );
return $items;
}
@@ -84,3 +89,62 @@ function _acquia_spi_drush_get() {
}
return acquia_spi_get();
}
+
+/**
+ * A command callback and drush wrapper for custom test validation.
+ *
+ */
+function drush_acquia_spi_custom_test_validate() {
+
+ $pass = array();
+ $failure = array();
+
+ $modules = module_implements('acquia_spi_test');
+ drush_print_r(dt("Acquia SPI custom tests were detected in: @modules \n", array('@modules' => implode(', ', $modules))));
+
+ foreach (module_implements('acquia_spi_test') as $module) {
+ $function = $module . '_acquia_spi_test';
+ if (function_exists($function)) {
+ $result = acquia_spi_test_validate($function());
+
+ if (!$result['result']) {
+ $failure[] = $module;
+ drush_print_r(dt("[FAILED] Validation failed for '@module' and has been logged.", array('@module' => $module)));
+ $custom_data[$module] = $result;
+
+ foreach ($result['failure'] as $test_name => $test_failures) {
+ foreach ($test_failures as $test_param => $test_value) {
+ $variables = array(
+ '!module_name' => $module,
+ '@message' => $test_value['message'],
+ '!param_name' => $test_param,
+ '!test_name' => $test_name,
+ '!value' => $test_value['value'],
+ );
+ drush_print_r(dt("[DETAILS] @message for parameter '!param_name'; current value '!value'. (Test !test_name in module !module_name)", $variables));
+ watchdog('acquia spi test', "Custom test validation failed: @message for parameter '!param_name'; current value '!value'. (Test '!test_name' in module '!module_name')", $variables);
+ }
+ }
+ }
+ else {
+ $pass[] = $module;
+ drush_print_r(dt("[PASSED] Validation passed for '@module.'", array('@module' => $module)));
+ }
+ }
+ drush_print_r("");
+ }
+
+ $message = 'Validation checks completed.';
+ if (count($pass) > 0) {
+ $variables['@passes'] = implode(', ', $pass);
+ $variables['@pass_count'] = count($pass);
+ $message .= "\n@pass_count module(s) passed validation: @passes.";
+ }
+ if (count($failure) > 0) {
+ $variables['@failures'] = implode(', ', $failure);
+ $variables['@fail_count'] = count($failure);
+ $message .= "\n@fail_count module(s) failed validation: @failures.";
+ }
+
+ drush_print_r(dt($message, $variables));
+}
diff --git a/acquia_spi/acquia_spi.install b/acquia_spi/acquia_spi.install
index 652ef4a..f9f116d 100644
--- a/acquia_spi/acquia_spi.install
+++ b/acquia_spi/acquia_spi.install
@@ -79,6 +79,43 @@ function acquia_spi_requirements($phase) {
'description' => t($description, array('!interval' => $interval, '!config-page' => $config_url, '!spi-send' => url('system/acquia-spi-send', array('query' => array('destination' => 'admin/reports/status', 'key' => $key))))),
);
}
+
+ // Acquia SPI custom tests status.
+ $variables = array(
+ '@help' => url('admin/help/acquia_agent'),
+ '@validate' => url('system/acquia-spi-custom-test-validate')
+ );
+
+ $modules = module_implements('acquia_spi_test');
+ if (empty($modules)) {
+ $description = 'No custom tests were detected in any module.
';
+ $value = 'Not implemented (more information)';
+ $severity = REQUIREMENT_OK;
+ }
+ else {
+ $result = acquia_spi_test_status();
+
+ if (!empty($result)) {
+ $modules = implode(', ', array_keys($result));
+ $description = 'Custom tests within the following module(s) have failed validation and will not be sent: %modules.
Please check the error logs for more information regarding how to pass validation or perform another validation check. (A validation check can also be performed via the Drush command, "spi-test-validate.")';
+ $value = 'Failed (more information)';
+ $severity = REQUIREMENT_ERROR;
+ }
+ else {
+ $modules = implode(', ', $modules);
+ $description = 'Custom test data is structured properly and is sending from: %modules';
+ $value = 'Passed';
+ $severity = REQUIREMENT_OK;
+ }
+
+ $variables['%modules'] = $modules;
+ }
+ $requirements['acquia_spi_test'] = array(
+ 'title' => t('Acquia Network SPI Custom Tests'),
+ 'description' => t($description, $variables),
+ 'value' => t($value, $variables),
+ 'severity' => $severity,
+ );
break;
}
diff --git a/acquia_spi/acquia_spi.module b/acquia_spi/acquia_spi.module
index 944ef64..f95d2ce 100644
--- a/acquia_spi/acquia_spi.module
+++ b/acquia_spi/acquia_spi.module
@@ -64,6 +64,13 @@ function acquia_spi_menu() {
'page callback' => '_acquia_spi_send',
'access callback' => '_acquia_spi_send_access',
);
+ $items['system/acquia-spi-custom-test-validate'] = array(
+ 'title' => 'Acquia SPI Custom Test Validation',
+ 'description' => 'Perform a validation check on all Acquia SPI custom tests.',
+ 'page callback' => 'acquia_spi_test_status',
+ 'page arguments' => array(TRUE),
+ 'access callback' => '_acquia_spi_send_access',
+ );
return $items;
}
@@ -558,6 +565,13 @@ function acquia_spi_get() {
if (!empty($security_review_results)) {
$additional_data['security_review'] = $security_review_results['security_review'];
}
+
+ // Collect all user-contributed custom tests that pass validation.
+ $custom_tests_results = acquia_spi_test_collect();
+ if (!empty($custom_tests_results)) {
+ $additional_data['custom_tests'] = $custom_tests_results;
+ }
+
$spi_data = module_invoke_all('acquia_spi_get');
if (!empty($spi_data)) {
foreach ($spi_data as $name => $data) {
@@ -669,6 +683,193 @@ function _acquia_spi_security_review_compatible() {
}
/**
+ * Collects all user-contributed test results that pass validation.
+ *
+ * @return array $custom_data
+ * An associative array containing properly formatted user-contributed tests.
+ *
+ */
+function acquia_spi_test_collect() {
+
+ $custom_data = array();
+
+ // Collect all custom data provided by hook_insight_custom_data().
+ $collections = module_invoke_all('acquia_spi_test');
+
+ foreach ($collections as $test_name => $test_params) {
+ $result = acquia_spi_test_validate(array($test_name => $test_params));
+
+ if ($result['result']) {
+ $custom_data[$test_name] = $test_params;
+ }
+ }
+
+ return $custom_data;
+}
+
+/**
+ * Implements hook_enable().
+ *
+ */
+function acquia_spi_test_enable() {
+
+ $modules = module_implements('acquia_spi_test');
+ foreach ($modules as $module) {
+ if (function_exists($module . '_acquia_spi_test')) {
+ drupal_set_message(t("An invokation of hook_acquia_spi_test() has been detected in @module.", array('@module' => $module)));
+ watchdog('acquia agent', "An invokation of hook_acquia_spi_test() has been detected in @module.", array('@module' => $module));
+ }
+ }
+}
+
+/**
+ * Implements hook_modules_enabled().
+ *
+ */
+function acquia_spi_test_modules_enabled($modules) {
+
+ foreach ($modules as $module) {
+ if (function_exists($module . '_acquia_spi_test')) {
+ drupal_set_message(t("A new invokation of hook_acquia_spi_test() has been detected in @module.", array('@module' => $module)));
+ watchdog('acquia spi test', "A new invokation of hook_acquia_spi_test() has been detected in @module.", array('@module' => $module));
+ }
+ }
+}
+
+
+/**
+ * Determines the status of all user-contributed tests and logs any failures to
+ * a tracking table.
+ *
+ * @param boolean $log
+ * (Optional) If TRUE, log all failures.
+ *
+ * @return array $custom_data
+ * An associative array containing any tests which failed validation.
+ *
+ */
+function acquia_spi_test_status($log = FALSE) {
+
+ $custom_data = array();
+
+ // Iterate through modules which contain hook_acquia_spi_test().
+ foreach (module_implements('acquia_spi_test') as $module) {
+ $function = $module . '_acquia_spi_test';
+ if (function_exists($function)) {
+ $result = acquia_spi_test_validate($function());
+
+ if (!$result['result']) {
+ $custom_data[$module] = $result;
+
+ foreach ($result['failure'] as $test_name => $test_failures) {
+ foreach ($test_failures as $test_param => $test_value) {
+ $variables = array(
+ '!module' => $module,
+ '@message' => $test_value['message'],
+ '!param_name' => $test_param,
+ '!test' => $test_name,
+ '!value' => $test_value['value'],
+ );
+
+ // Only log if we're performing a full validation check.
+ if ($log) {
+ drupal_set_message(t("Custom test validation failed for !test in !module and has been logged: @message for parameter '!param_name'; current value '!value'.", $variables), 'error');
+ watchdog('acquia spi test', "Custom test validation failed: @message for parameter '!param_name'; current value '!value'. (Test '!test_name' in module '!module_name')", $variables, WATCHDOG_WARNING);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If a full validation check is being performed, go to the status page to
+ // show the results.
+ if ($log) {
+ drupal_goto('admin/reports/status');
+ }
+
+ return $custom_data;
+}
+
+/**
+ * Validates data from custom test callbacks.
+ *
+ * @param array $collection
+ * An associative array containing a collection of user-contributed tests.
+ *
+ * @return array
+ * An associative array containing the validation result of the given tests,
+ * along with any failed parameters.
+ *
+ */
+function acquia_spi_test_validate($collection) {
+
+ $result = TRUE;
+ $check_result_value = array();
+
+ // Load valid categories and severities.
+ $categories = array('performance', 'security', 'best_practices');
+ $severities = array(0, 1, 2, 4, 8, 16, 32, 64, 128);
+
+ foreach ($collection as $machine_name => $tests) {
+ foreach ($tests as $check_name => $check_value) {
+ $fail_value = '';
+ $message = '';
+
+ $check_name = strtolower($check_name);
+ $check_value = (is_string($check_value)) ? strtolower($check_value) : $check_value;
+
+ // Validate the data inputs for each check.
+ switch ($check_name) {
+ case 'category':
+ if (!is_string($check_value) || !in_array($check_value, $categories)) {
+ $type = gettype($check_value);
+ $fail_value = "$check_value ($type)";
+ $message = 'Value must be a string and one of ' . implode(', ', $categories);
+ }
+ break;
+
+ case 'solved':
+ if (!is_bool($check_value)) {
+ $type = gettype($check_value);
+ $fail_value = "$check_value ($type)";
+ $message = 'Value must be a boolean';
+ }
+ break;
+
+ case 'severity':
+ if (!is_int($check_value) || !in_array($check_value, $severities)) {
+ $type = gettype($check_value);
+ $fail_value = "$check_value ($type)";
+ $message = 'Value must be an integer and set to one of ' . implode(', ', $severities);
+ }
+ break;
+
+ default:
+ if (!is_string($check_value) || strlen($check_value) > 1024) {
+ $type = gettype($check_value);
+ $fail_value = "$check_value ($type)";
+ $message = 'Value must be a string';
+ }
+ break;
+ }
+
+ if (!empty($fail_value) && !empty($message)) {
+ $check_result_value['failed'][$machine_name][$check_name]['value'] = $fail_value;
+ $check_result_value['failed'][$machine_name][$check_name]['message'] = $message;
+ }
+ }
+ }
+
+ // If there were any failures, the test has failed. Into exile it must go.
+ if (!empty($check_result_value)) {
+ $result = FALSE;
+ }
+
+ return array('result' => $result, 'failure' => (isset($check_result_value['failed'])) ? $check_result_value['failed'] : array());
+}
+
+/**
* Attempt to determine the version of Drupal being used.
* Note, there is better information on this in the common.inc file.
*