'Automated testing', 'description' => 'Configure the automated testing project issue integration.', 'page callback' => 'drupal_get_form', 'page arguments' => array('pift_admin_settings_form'), 'access arguments' => array('administer projects'), 'file' => 'pift.admin.inc', ); $items['pift/retest/%'] = array( 'title' => 'Request retest of a file or branch', 'page callback' => 'drupal_get_form', 'page arguments' => array('pift_pages_retest_confirm_form', 2), 'access arguments' => array('pift re-test files'), 'file' => 'pift.pages.inc', 'type' => MENU_CALLBACK, ); $items['pift/delete/%'] = array( 'title' => 'Request deletion of a test', 'page callback' => 'drupal_get_form', 'page arguments' => array('pift_pages_delete_test_confirm_form', 2), 'access arguments' => array('pift re-test files'), 'file' => 'pift.pages.inc', 'type' => MENU_CALLBACK, ); // TODO: The plan was to remove the testing-status tab for the D7 Port, and // add it back in with Conduit. However, we need somewhere to store the // 'enable testing' checkbox, so leaving this in temporarily until the Conduit // piece is available. $items['node/%node/qa-settings'] = array( 'title' => t('Automated Testing'), 'access callback' => 'pift_results_visibility', 'access arguments' => array(1), 'page callback' => 'drupal_get_form', 'page arguments' => array('pift_pages_project_issue_settings', 1), 'file' => 'pift.pages.inc', 'weight' => '5', 'type' => MENU_LOCAL_TASK, ); return $items; } /** * Access callback to determine whether the Automated Testing tab is visible. */ function pift_results_visibility($node) { // Only display the tab on projects with releases if (project_node_is_project($node)) { if ($node->field_project_has_releases[$node->language][0]['value']) { // Ensure user has access to enable testing on the node if (user_access('pift enable project testing')) { return TRUE; } } } return FALSE; } /** * Implements hook_permission(). */ function pift_permission() { return array( 'pift re-test files' => array( 'title' => t('pift re-test files'), 'description' => t('Request a file to be re-tested'), ), 'pift enable project testing' => array( 'title' => t('pift enable project testing'), 'description' => t('Enable testing on a project'), ), 'pift access project testing tab' => array( 'title' => t('pift access project testing tab'), 'description' => t('Access the testing tab on projects'), ), ); } /** * Implements hook_theme(). */ function pift_theme() { return array( 'pift_attachments' => array( 'variables' => array( 'files' => array(), 'closed' => FALSE, ), 'file' => 'pift.pages.inc', ), 'pift_auto_followup' => array( 'variables' => array( 'type' => '', 'nid' => 0, 'cid' => 0, 'filename' => '', ), ), ); } /** * Implements hook_init(). */ function pift_init() { drupal_add_css(drupal_get_path('module', 'pift') . '/pift.css'); } /** * Implements hook_cron(). */ function pift_cron() { if (PIFT_DELETE) { // An issue comment or node has been deleted, remove related test entries. pift_test_delete_files(); variable_set('pift_delete', FALSE); } // Check if sending is enabled and that the sending frequency has elapsed. $time = REQUEST_TIME; if (PIFT_FREQUENCY != -1 && $time > PIFT_LAST + PIFT_FREQUENCY) { module_load_include('cron.inc', 'pift'); // Requeue all tests that have passed the re-test interval. // pift_cron_retest(); TODO fix query. // Send a batch of queued tests. pift_cron_queue_batch(); // Retrieve any results that have occured since last cron run. pift_cron_retrieve_results(); // Store current time as last run. variable_set('pift_last', $time); } } /** * Implements hook_versioncontrol_code_arrival(). */ function pift_versioncontrol_code_arrival(VersioncontrolRepository $repository, VersioncontrolEvent $event) { // Ignore events for disabled projects and non-Git repos. if (!$repository instanceof VersioncontrolGitRepository || !pift_project_enabled($repository->project_nid)) { return; } module_load_include('cron.inc', 'pift'); $api_versions = pift_core_api_versions(); $branch_names = array(); foreach ($event as $ref) { if (VERSIONCONTROL_GIT_REFTYPE_BRANCH === $ref->reftype) { $branch_names[] = $ref->refname; } } $rids = pift_cron_get_release($repository->project_nid, $branch_names); foreach ($rids as $rid) { // Ensure that one of the compatibility terms is present on the release node. $release = node_load($rid); $found = FALSE; // TODO: Update loop, since $release->taxonomy will not exist foreach ($api_versions as $api_version) { if (array_key_exists($api_version, $release->taxonomy)) { // Compatible term found, continue processing. $test_id = db_query('SELECT test_id FROM {pift_test} WHERE type = :type AND id = :id', array(':type' => PIFT_TYPE_RELEASE, ':id' => $rid))->fetchField(); // If existing test for release, queue it, otherwise add a new test. if ($test_id) { pift_test_requeue($test_id); } else if ($test_id !== 0) { pift_test_add(PIFT_TYPE_RELEASE, $rid); } break; } } } } /** * Helper fuction to build a list of releases for this project's repository. * * @param $node The project node object * @param $quiet Silence error messages * @return array List of available labels with corresponding release nodes */ function pift_get_releases($node, $quiet = FALSE) { $valid = pift_valid_prefix_list(); $branches = array(); $topbranches = array(); if (project_node_is_project($node)) { $result = db_query("Select b.name from {versioncontrol_release_labels} a join {versioncontrol_labels} b on a.label_id = b.label_id where a.project_nid = :nid", array(':nid' => $node->nid)); foreach ($result as $data) { // Filter out any branches < 6 (not accepted by PIFT) // Move '.x' releases to the top if (in_array(substr($data->name, 0, 3), $valid)) { if (substr($data->name, strlen($data->name) - 2, 2) == '.x') { $topbranches[$data->name] = $data->name; } else { $branches[$data->name] = $data->name; } } } // Sort to get the highest branch on top uasort($branches, 'version_compare'); uasort($topbranches, 'version_compare'); $branches = array_merge($branches, $topbranches); $branches = pift_array_reverse($branches); if (empty($branches) && !$quiet) { drupal_set_message(t('No releases found for the given project.'), 'error', FALSE); } } return $branches; } /** * Returns a listing of valid project release prefixes (ie. 6.x, 7.x, 8.x) */ function pift_valid_prefix_list() { $terms = array(); $tids = variable_get('pift_core', array()); foreach ($tids as $key => $value) { if (!empty($tids[$key])) { $term = taxonomy_term_load($key); $terms[$key] = $term->name; } } return $terms; } /** * Alternative function for the php standard array_reverse() * * As array_reverse() would damage numerical array keys, from * http://drupal.org/node/1074220. Code copied from project_git_instructions * * Borrowed from http://php.net/manual/en/function.array-reverse.php#102492 */ function pift_array_reverse($array) { $array_key = array_keys($array); $array_value = array_values($array); $array_return = array(); for ($i = 1, $size_of_array = sizeof($array_key); $i <= $size_of_array; $i++) { $array_return[$array_key[$size_of_array -$i]] = $array_value[$size_of_array -$i]; } return $array_return; } /** * Implements hook_form_FORM_ID_alter(). */ function pift_form_project_issue_node_form_alter(&$form, $form_state, $form_id) { module_load_include('pages.inc', 'pift'); pift_pages_description_add($form, $form_state, $form_id); } /** * Implements hook_node_view(). * * TODO: This approach is no longer valid after the project* changes. The * new approach will be to implement a field formatter on the field_issue_files * field, and add the pift testing results via this field formatter; which * should be much cleaner than removing and injecting the file attachments * table as was done in D6. */ //function pift_node_view($node, $view_mode = 'full') { // if (pift_node_is_interesting($node)) { // if (!$a3 && pift_project_enabled($node->project_issue['pid'])) { // Full view. // $files = pift_test_get_files_node($node->nid); // $status = $node->project_issue['sid']; // $node->content['pift_files'] = array( // '#value' => '