'Project release integration', 'description' => 'Configure how project release nodes will be integrated with version control systems.', 'page callback' => 'drupal_get_form', 'page arguments' => array('versioncontrol_release_admin_form'), 'access arguments' => array('administer version control systems'), 'type' => MENU_LOCAL_TASK, ); return $items; } /** * Form callback for 'admin/project/versioncontrol-settings/project-release': * Global settings for this module. */ function versioncontrol_release_admin_form(&$form_state) { $form['versioncontrol_release_message_new_release_branch'] = array( '#title' => t('Message when new releases are added from a branch'), '#type' => 'textarea', '#default_value' => variable_get('versioncontrol_release_message_new_release_branch', ''), '#description' => t('The message to show to project maintainers when they add a new development snapshot release from a branch. Leave empty to not show any specific message.'), ); $form['versioncontrol_release_message_new_release_tag'] = array( '#title' => t('Message when new releases are added from a tag'), '#type' => 'textarea', '#default_value' => variable_get('versioncontrol_release_message_new_release_tag', ''), '#description' => t('The message to show to project maintainers when they add a new official release from a tag. Leave empty to not show any specific message.'), ); return system_settings_form($form); } /** * Implement hook_ctools_plugin_directory(). */ function versioncontrol_release_ctools_plugin_directory($module, $plugin) { if ($module == 'versioncontrol_release' && $plugin == 'label_version_mapper') { return "plugins/$plugin"; } } /** * Instantiate an object to map VCAPI labels into release node version objects. * * This function looks in the project's VCAPI repository object for the * 'plugins' array and finds the desired CTools plugin for the label/version * mapping. It then loads the plugin and instantiates a mapper object. If * anything goes wrong, we return FALSE. * * @param $project_node * The fully loaded node object for the project we're doing the mapping for. * * @return object * The specific elements of the version that a given VCAPI label maps to. * Can use any of the following fields: 'version_major', 'version_minor', * 'verion_patch', 'version_extra', or 'version_api_tid'. * * @see project_release_get_version() * @see VersioncontrolReleaseLabelVersionMapperInterface */ function versioncontrol_release_get_label_version_mapper($project_node) { ctools_include('plugins'); if (!empty($project_node->versioncontrol_project['repo'])) { $repo = $project_node->versioncontrol_project['repo']; if (!empty($repo->plugins['versioncontrol_release_label_version_mapper'])) { $mapper_plugin = $repo->plugins['versioncontrol_release_label_version_mapper']; } else { $mapper_plugin = variable_get('versioncontrol_release_label_version_mapper', ''); } if (empty($mapper_plugin)) { $mapper_plugin = 'generic'; } if ($class = ctools_plugin_load_class('versioncontrol_release', 'label_version_mapper', $mapper_plugin, 'mapper')) { $mapper = new $class(); return $mapper; } } return FALSE; } /** * Map a VCAPI tag into an object of version information. * * @param string $tag_name * The name of a version control tag. * @param $project_node * The fully loaded node object for the project we're doing the mapping for. * * @return object * The specific elements of the version that a given VCAPI label maps to. * Can use any of the following fields: 'version_major', 'version_minor', * 'verion_patch', 'version_extra', or 'version_api_tid'. * * @see project_release_get_version() * @see versioncontrol_release_get_label_version_mapper() * @see VersioncontrolReleaseLabelVersionMapperInterface */ function versioncontrol_release_get_version_from_tag($tag_name, $project_node) { $mapper = versioncontrol_release_get_label_version_mapper($project_node); if (!empty($mapper)) { $version = $mapper->GetVersionFromTag($tag_name, $project_node); if (!empty($version)) { $version->pid = $project_node->nid; } return $version; } return FALSE; } /** * Map a VCAPI branch into an object of version information. * * @param string $branch_name * The name of the version control branch. * @param $project_node * The fully loaded node object for the project we're doing the mapping for. * * @return object * The specific elements of the version that a given VCAPI label maps to. * Can use any of the following fields: 'version_major', 'version_minor', * 'verion_patch', 'version_extra', or 'version_api_tid'. * * @see project_release_get_version() * @see versioncontrol_release_get_label_version_mapper() * @see VersioncontrolReleaseLabelVersionMapperInterface */ function versioncontrol_release_get_version_from_branch($branch_name, $project_node) { $mapper = versioncontrol_release_get_label_version_mapper($project_node); if (!empty($mapper)) { $version = $mapper->GetVersionFromBranch($branch_name, $project_node); if (!empty($version)) { $version->pid = $project_node->nid; } return $version; } return FALSE; } /** * Map a VCAPI branch into an object of version information. * * @param string $label_name * The name of the version control label. * @param integer $label_type * The VCS label types constant indicating if the label is a branch or tag. * @param $project_node * The fully loaded node object for the project we're doing the mapping for. * * @return object * The specific elements of the version that a given VCAPI label maps to. * Can use any of the following fields: 'version_major', 'version_minor', * 'verion_patch', 'version_extra', or 'version_api_tid'. * * @see project_release_get_version() * @see versioncontrol_release_get_label_version_mapper() * @see VersioncontrolReleaseLabelVersionMapperInterface */ function versioncontrol_release_get_version_from_label($label_name, $label_type, $project_node) { $mapper = versioncontrol_release_get_label_version_mapper($project_node); if (!empty($mapper)) { $version = $mapper->GetVersionFromLabel($label_name, $label_type, $project_node); if (!empty($version)) { $version->pid = $project_node->nid; } return $version; } return FALSE; } /** * Return an array of labels that are still unused for the given project, * with the label_id of each label array used as array key. */ function versioncontrol_release_get_possible_labels($project_node) { if (empty($project_node->versioncontrol_project['repo'])) { return array(); } $labels = array(); $tags_t = t('Tags'); $branches_t = t('Branches'); $repo = $project_node->versioncontrol_project['repo']; $tags = $repo->loadTags(array(), array(), array('callback' => 'versioncontrol_release_load_labels_query_alter')); if (!empty($tags)) { foreach ($tags as $tag) { $version = versioncontrol_release_get_version_from_tag($tag->name, $project_node); if (!empty($version)) { $label_text = versioncontrol_project_get_label_caption($tag->name, $version, $project_node); $labels[$tags_t][$tag->label_id] = $label_text; } } } $branches = $repo->loadBranches(array(), array(), array('callback' => 'versioncontrol_release_load_labels_query_alter')); if (!empty($branches)) { foreach ($branches as $branch) { $version = versioncontrol_release_get_version_from_branch($branch->name, $project_node); if (!empty($version)) { $label_text = versioncontrol_project_get_label_caption($branch->name, $version, $project_node); $labels[$branches_t][$branch->label_id] = $label_text; } } } return $labels; } /** * Return a caption to use for a given VCAPI label and its version. * * If the version string exactly matches the label, just print the label name * itself. If the version string is different, include that in parenthesis * after the label name. * * @param string $label_name * The name of the label. * @param stdClass $version * The project_release version object that corresponds to the label. * @param $project_node * The fully-loaded project node we're operating on. * * @return string * The caption to use for the label and its version. */ function versioncontrol_project_get_label_caption($label_name, $version, $project_node) { $label_text = ''; $version_string = project_release_get_version($version, $project_node); if ($version_string != $label_name) { $label_text = t('!name (!version)', array('!name' => $label_name, '!version' => $version_string)); } else { $label_text = $label_name; } return $label_text; } /** * Callback function to alter the query when loading VCAPI labels. * * When we're generating the list of available labels for the release node * form, we need to filter out any labels that already have a release node * associated with them. So we LEFT JOIN on {versioncontrol_release_labels} * and ensure that the label_id is NULL in from that table. */ function versioncontrol_release_load_labels_query_alter(&$query, $ids, $conditions, $options) { $query->leftjoin('versioncontrol_release_labels', 'vcrl', 'base.label_id = vcrl.label_id'); $query->isNull('vcrl.label_id'); } /** * Implementation of hook_form_alter(). * * We do this instead of hook_form_[form_id]_alter() because it gets called * later, which means we can really unset all elements added to the forms. * (As required by the label selector form for the "add" form.) */ function versioncontrol_release_form_alter(&$form, &$form_state, $form_id) { if ($form_id == 'project_release_node_form') { // Use separate methods for the add and edit versions of this form. if (arg(1) == 'add') { versioncontrol_release_project_release_form_alter_add($form, $form_state); } else { versioncontrol_release_project_release_form_alter_edit($form, $form_state); } } } /** * Alter the form for adding a project_release node. * * @see versioncontrol_release_form_alter() */ function versioncontrol_release_project_release_form_alter_add(&$form, &$form_state) { $project_node = $form['project']['#value']; if (empty($project_node->project_release['releases'])) { return; // This project does not support releases, nothing to alter. } // Check to see if this project has a repository set. If not, then don't // alter the form so that the project can have releases just as if // versioncontrol_releases.module were not enabled at all. if (empty($project_node->versioncontrol_project['repo'])) { return; } if (isset($form_state['storage']['versioncontrol_release_label_id'])) { $label_id = $form_state['storage']['versioncontrol_release_label_id']; } if (isset($form['#versioncontrol_release_label_id'])) { $label_id = $form['#versioncontrol_release_label_id']; } if (!empty($label_id)) { $label = db_fetch_array(db_query( 'SELECT label_id, repo_id, name, type FROM {versioncontrol_labels} WHERE label_id = %d', $label_id )); } if (empty($label)) { // Page #1: No release tag or branch has been selected yet. // Clear the whole form in favor of a simple label selector. versioncontrol_release_project_release_form_alter_add_select_label($form, $form_state, $project_node); } else { // Page #2: The release tag or branch has been selected. // Alter the "add" form accordingly. $form['#versioncontrol_release_label_id'] = $label_id; versioncontrol_release_project_release_form_alter_add_node_form($form, $form_state, $project_node, $label); } } /** * Alter the release node add form: page #1 to select a branch or tag. * * @see versioncontrol_release_project_release_form_alter_add() */ function versioncontrol_release_project_release_form_alter_add_select_label(&$form, &$form_state, $project_node) { // Rip out everything else that might be in this form. _versioncontrol_release_project_release_form_alter_unset_all($form); unset($form['validate_version']); // Gather possible values for a label selector. $labels = versioncontrol_release_get_possible_labels($project_node); if (!empty($labels)) { $repo = $project_node->versioncontrol_project['repo']; $backend = $repo->getBackend(); $form['versioncontrol_release'] = array( '#type' => 'markup', '#value' => '', '#weight' => -4, ); $form['versioncontrol_release']['versioncontrol_release_label_id'] = array( '#type' => 'select', '#title' => t('!backend_name release tag or branch', array('!backend_name' => $backend->name)), '#options' => $labels, '#required' => TRUE, '#description' => t('Select the !backend_name tag or branch (and therefore version number) for this release.', array('!backend_name' => $backend->name)), ); } else { $err = t('There are no tags or branches for this project that do not already have a release associated with them.'); $err .= '

' . t('To create a release, you must first create either a new tag on one of the existing branches for this project, or you must add a new branch.') . '

'; $err .= '

' . t('Once you have created a tag or branch that should be used for your new release, try pressing the %retry link to continue.', array('%retry' => t('Retry'))) . '

'; $form['error'] = array( '#type' => 'markup', '#prefix' => '
', '#value' => $err, '#suffix' => '
', ); unset($form['buttons']['preview']); $form['retry'] = array( '#type' => 'markup', '#value' => l(t('Retry'), 'node/add/project_release/' . $project_node->nid), ); } } /** * Unset all the elements on the release node form. * * Used when altering the release node form into a multi-step form. */ function _versioncontrol_release_project_release_form_alter_unset_all(&$form, $whitelist = array()) { foreach (element_children($form) as $child) { if ($child != 'buttons' && (empty($form[$child]['#type']) || ($form[$child]['#type'] != 'hidden' && $form[$child]['#type'] != 'value' && $form[$child]['#type'] != 'token' && (!in_array($child, $whitelist))))) { $form[$child]['#access'] = FALSE; } } // Change the "Preview" button to "Next" and hide the "Save" button. $form['buttons']['preview'] = array( '#type' => 'submit', '#value' => t('Next'), '#weight' => 50, '#submit' => array('versioncontrol_release_form_next_submit'), ); $form['buttons']['submit']['#access'] = FALSE; } /** * Submit callback for the "Next" button on the release label selection form. */ function versioncontrol_release_form_next_submit($form, &$form_state) { if (isset($form_state['values']['versioncontrol_release_label_id'])) { $form_state['storage']['versioncontrol_release_label_id'] = $form_state['values']['versioncontrol_release_label_id']; } $project = $form['project']['#value']; $form_state['storage']['project_release']['pid'] = $project->nid; $vocab_id = _project_release_get_api_vid(); if (isset($form_state['values']['taxonomy'][$vocab_id])) { $form_state['storage']['project_release']['version_api_tid'] = $form_state['values']['taxonomy'][$vocab_id]; } foreach (array('version', 'version_major', 'version_minor', 'version_patch', 'version_extra') as $field) { if (empty($form_state['storage']['project_release'][$field]) && isset($form_state['values']['project_release'][$field])) { $form_state['storage']['project_release'][$field] = $form_state['values']['project_release'][$field]; $form_state['node']['project_release'][$field] = $form_state['values']['project_release'][$field]; } } $form_state['rebuild'] = TRUE; } /** * Alter the release node add form: page #2 once the branch/tag is known. * * @see versioncontrol_release_project_release_form_alter_add() */ function versioncontrol_release_project_release_form_alter_add_node_form(&$form, &$form_state, $project_node, $label) { $fields = array('version_major', 'version_minor', 'version_patch'); $vocab_id = _project_release_get_api_vid(); ///TODO: private function? baaad. $repo = $project_node->versioncontrol_project['repo']; $backend = $repo->getBackend(); $label_type_string = ($label['type'] == VERSIONCONTROL_OPERATION_TAG) ? t('tag') : t('branch'); $existing = db_fetch_object(db_query("SELECT vcrl.release_nid, vcrl.label_id, vcl.name, vcl.type FROM {versioncontrol_release_labels} vcrl INNER JOIN {versioncontrol_labels} vcl ON vcrl.label_id = vcl.label_id WHERE vcrl.project_nid = %d AND vcrl.label_id = %d", $project_node->nid, $label['label_id'])); if (!empty($existing)) { $label['type'] = $existing->type; $label['name'] = $existing->name; _versioncontrol_release_project_release_form_existing_release($form, $existing->release_nid, $project_node->nid, 'label', $label); return; } // {project_release_nodes} holds these fields. Even though the cannonical // storage for this data is in {versioncontrol_release_labels} and friends, // we want to keep the denormalized copies updated in {project_release_nodes} // so that various project_release related queries don't have to know about // VCAPI and/or create a bunch of additional JOINs to complicate the queries. $form['project_release']['rebuild'] = array( '#type' => 'value', '#value' => $label['type'] == VERSIONCONTROL_OPERATION_BRANCH, ); $form['project_release']['tag'] = array( '#type' => 'value', '#value' => $label['name'], ); $version = versioncontrol_release_get_version_from_label($label['name'], $label['type'], $project_node); if (!empty($version)) { $version_string = project_release_get_version($version, $project_node); $existing_nid = project_release_exists($version); if (!empty($existing_nid)) { _versioncontrol_release_project_release_form_existing_release($form, $existing_nid, $project_node->nid, 'version', $version_string); return; } // Stash this in a form value so it'll make it through to validation // where the title of the release node is set. $form['#versioncontrol_release_version'] = array_merge( (array) $version, array('version' => $version_string) ); } else { // We should never get here, since we already filter out labels that don't // produce valid version objects on the first page of the form. } $form['versioncontrol_release'] = array( '#type' => 'markup', '#value' => '', '#weight' => -4, ); // Force the label that was already selected. $label_options[$label['label_id']] = $label['name']; $form['versioncontrol_release']['versioncontrol_release_label_id'] = array( '#type' => 'select', '#title' => t('!backend_name !label_type', array('!backend_name' => $backend->name, '!label_type' => $label_type_string)), '#options' => $label_options, '#default_value' => $label['label_id'], '#required' => TRUE, ); if (!empty($version_string)) { // Since this is the final page, turn this into a fieldset with all the // nice float/clear goodness. $form['versioncontrol_release']['#type'] = 'fieldset'; $form['versioncontrol_release']['#collapsible'] = TRUE; $form['versioncontrol_release']['#title'] = t('Release identification'); $form['versioncontrol_release']['#prefix'] = '
'; $form['versioncontrol_release']['#suffix'] = '
'; $form['versioncontrol_release']['#description'] = t('Now that the !labeltype has been selected, these can not be modified unless you go back to the previous page.', array('!labeltype' => $label_type_string, '@url' => url('node/add/project-release/' . $project_node->nid))); // Display the version string so the user knows we've got it right. $form['versioncontrol_release']['version'] = array( '#type' => 'textfield', '#title' => 'Version string', '#default_value' => $version_string, '#attributes' => array('readonly' => TRUE, 'style' => 'width: auto;'), '#required' => TRUE, '#size' => 30, '#maxlength' => 40, ); // Also stash it in form_state['storage'] so we preserve it through // multi-page, preview, and submit. $form_state['storage']['project_release']['version'] = $version_string; } if (!empty($version)) { foreach (array_merge($fields, array('version_extra')) as $field) { if (isset($version->$field)) { $form_state['storage']['project_release'][$field] = $version->$field; } $form['project_release'][$field]['#access'] = FALSE; } // If we know the version compatibility taxonomy term, rip out all other // taxonomy options for that vocabulary. if (isset($version->version_api_tid) && isset($form['taxonomy'][$vocab_id])) { $version_api_term_id = $version->version_api_tid; $indexes = form_get_options($form['taxonomy'][$vocab_id], $version_api_term_id); if ($indexes !== FALSE) { $options = array(); foreach ($indexes as $index) { $options[] = $form['taxonomy'][$vocab_id]['#options'][$index]; } $form['taxonomy'][$vocab_id]['#options'] = $options; $form['taxonomy'][$vocab_id]['#default_value'] = $version_api_term_id; } } // This hides the (now empty) "Version number elements" fieldset. $form['project_release']['#access'] = FALSE; // For the actual submit button, add a handler to clear out // $form_state['storage'] entirely so that we actually submit the form. $form['buttons']['submit']['#submit'][] = 'versioncontrol_release_form_add_final_submit'; array_unshift($form['#validate'], 'versioncontrol_release_form_add_validate'); } // Remove project_release's file selector, since if we're doing this via a // release label, the file will be filled in later by the packaging script. // TODO (feature): it'd be nice if this was optional, so that some // sites might want to still allow file attachments for releases... unset($form['project_release_files']); } /** * Convert the release node form into an error about an existing release. * * @param array $form * Reference to the release node form to alter. * @param integer $existing_release_nid * The node ID of the existing release node that would be duplicated. * @param integer $project_nid * The node ID of the project that the release node form is for. * @param string $error_type * The cause of the duplicate error. Can be either 'label' or 'version'. * @param $identifier * The identifier for the given $error_type, either a label array or a * version string. * * @see versioncontrol_release_project_release_form_alter_add_node_form() */ function _versioncontrol_release_project_release_form_existing_release(&$form, $existing_release_nid, $project_nid, $error_type, $identifier) { foreach (element_children($form) as $key) { unset($form[$key]); } if ($error_type == 'label') { $substitutions = array( '%label_name' => $identifier['name'], '@release_url' => url('node/' . $existing_release_nid), ); if ($identifier['type'] == VERSIONCONTROL_OPERATION_TAG) { $error_message = t('The tag you have selected (%label_name) is already in use by another release.', $substitutions); } else { $error_message = t('The branch you have selected (%label_name) is already in use by another release.', $substitutions); } } else { $error_message = t('The version you have selected (%version_string) is already in use by another release.', array('%version_string' => $identifier, '@release_url' => url('node/' . $existing_release_nid))); } $form['error'] = array( '#type' => 'markup', '#prefix' => '
', '#value' => $error_message, '#suffix' => '
', ); $form['try_again'] = array( '#type' => 'markup', '#value' => t('You can try again.', array('@add_release_url' => url('node/add/project-release/' . $project_nid))), ); } /** * Validation handler for 2nd page of the release node add form. * * By saving the version string into $form_state, the release node will * have the right title according to the version string once saved. */ function versioncontrol_release_form_add_validate($form, &$form_state) { $form_state['values']['project_release'] = array_merge( $form_state['values']['project_release'], $form['#versioncontrol_release_version'] ); } /** * Submit handler for the "Save" button on the release node form. * * This handler is invoke when the release node is finally being saved, and * unsets the $form_state['storage'] values that have been set in * versioncontrol_release_form_next_submit(), so that the form will be * submitted instead of being rebuilt. */ function versioncontrol_release_form_add_final_submit($form, &$form_state) { unset($form_state['storage']['versioncontrol_release_label_id']); unset($form_state['storage']['project_release']); } /** * Implementation of hook_nodeapi(): * * Load the release label info into $node->versioncontrol_release if there is * a release for this node, and insert or delete the release label when the * node is being added or deleted. */ function versioncontrol_release_nodeapi(&$node, $op, $arg = NULL) { if ($node->type == 'project_release') { switch ($op) { case 'load': $label = versioncontrol_release_get_release_label($node->nid); if (!empty($label)) { $node->versioncontrol_release = array('label' => $label); } return; case 'view': if (!empty($node->versioncontrol_release)) { $label = $node->versioncontrol_release['label']; if ($label['type'] == VERSIONCONTROL_OPERATION_BRANCH) { $output = t('Development snapshot from branch: @branch', array('@branch' => $label['name'])); } else { $output = t('Official release from tag: @tag', array('@tag' => $label['name'])); } $node->content['versioncontrol_release'] = array( '#value' => '' . $output . '
', '#weight' => -3, ); } return; case 'insert': // The node array possibly contains versioncontrol_release_label_id // as submit value of the (form_altered) node add form. if (isset($node->versioncontrol_release_label_id)) { $label = versioncontrol_release_insert_release_label( $node->nid, $node->versioncontrol_release_label_id, $node->project_release['pid'] ); unset($node->versioncontrol_release_label_id); $node->versioncontrol_release = array('label' => $label); if ($op == 'insert') { // and not 'update' // Show the admin-defined insertion message to the user. $msg_variable = ($label['type'] == VERSIONCONTROL_OPERATION_TAG) ? 'versioncontrol_release_message_new_release_branch' : 'versioncontrol_release_message_new_release_tag'; $msg = variable_get($msg_variable, ''); if (!empty($msg)) { drupal_set_message($msg); } } } return; case 'delete': versioncontrol_release_delete_release_label($node->nid); return; default: return; } } } /** * Fetch an array describing the VCAPI label associated with a release. * * @param integer $release_nid * The node ID of the release to retrieve label data for. * * @return * An array of values describing the VCAPI label associated with a release, * or FALSE if there's no label. */ function versioncontrol_release_get_release_label($release_nid) { $result = db_query("SELECT vcl.label_id, vcl.name, vcl.type FROM {versioncontrol_labels} vcl INNER JOIN {versioncontrol_release_labels} vcrl ON vcl.label_id = vcrl.label_id WHERE vcrl.release_nid = %d", $release_nid); $label = db_fetch_array($result); return $label; } /** * Associate a release node with a VCS label. * * @param integer $release_nid * The node ID of the release node. * @param integer $label_id * The {versioncontrol_labels}.label_id of the VCAPI label. * @param integer $project_nid * The node ID of the project node the release is associated with. * * @return * The result of versioncontrol_release_get_release_label(). * * @see versioncontrol_release_get_release_label(). */ function versioncontrol_release_insert_release_label($release_nid, $label_id, $project_nid) { db_query('INSERT INTO {versioncontrol_release_labels} (release_nid, label_id, project_nid) VALUES (%d, %d, %d)', $release_nid, $label_id, $project_nid); return versioncontrol_release_get_release_label($release_nid); } /** * Delete the associated release label for the given release node. * * @param integer $release_nid * The node ID of the release node. */ function versioncontrol_release_delete_release_label($release_nid) { db_query('DELETE FROM {versioncontrol_release_labels} WHERE release_nid = %d', $release_nid); } /** * Alter the project release node edit form. */ function versioncontrol_release_project_release_form_alter_edit(&$form, &$form_state) { $release_node = $form['#node']; $project_node = node_load($release_node->project_release['pid']); // If the project doesn't have a versioncontrol repo, nothing to alter. if (empty($project_node->versioncontrol_project['repo'])) { return; } $repo = $project_node->versioncontrol_project['repo']; $backend = $repo->getBackend(); $release_label = $release_node->versioncontrol_release['label']; $label_type_string = ($release_label['type'] == VERSIONCONTROL_OPERATION_TAG) ? t('tag') : t('branch'); // Special case to allow editing HEAD/master releases to move them to more // specific versions. if ($release_label['type'] == VERSIONCONTROL_OPERATION_BRANCH) { $ambiguous_labels = variable_get('versioncontrol_release_ambiguous_lables', array('HEAD', 'master')); if (in_array($release_label['name'], $ambiguous_labels)) { $branches = $repo->loadBranches(array(), array(), array('callback' => 'versioncontrol_release_load_labels_query_alter')); if (!empty($branches)) { foreach ($branches as $branch) { $version = versioncontrol_release_get_version_from_branch($branch->name, $project_node); $version_string = project_release_get_version($version, $project_node); if ($version_string == $release_node->project_release['version']) { $label_options = array( $release_label['label_id'] => $release_label['name'], $branch->label_id => $branch->name, ); $form['buttons']['submit']['#submit'][] = 'versioncontrol_release_project_release_form_edit_submit'; break; } } } } } // Hide whatever project_release thinks. unset($form['rel_id']['tag']); // Add a value so that project_release isn't confused in its own submit // handler. If we hit the special case of moving a HEAD/master release to a // more specific branch, we'll update this record ourselves. $form['project_release']['tag'] = array( '#type' => 'value', '#value' => $release_label['name'], ); // Add our own element. if (!empty($label_options)) { // We have multiple options for the label, so include a selector. $form['rel_id']['vcs_label'] = array( '#type' => 'select', '#title' => t('!backend_name !label_type', array('!backend_name' => $backend->name, '!label_type' => $label_type_string)), '#default_value' => $release_label['label_id'], '#options' => $label_options, ); } else { // Default case of the existing label, so present a disabled text field. $form['rel_id']['vcs_label'] = array( '#type' => 'textfield', '#title' => t('!backend_name !label_type', array('!backend_name' => $backend->name, '!label_type' => $label_type_string)), '#default_value' => $release_label['name'], '#value' => $release_label['name'], '#attributes' => array('disabled' => 'disabled'), '#required' => TRUE, '#size' => strlen($release_label['name']) + 5, ); } // If the version is identical to the VCS label, hide the version. if ($form['rel_id']['version']['#default_value'] == $release_label['name']) { $form['rel_id']['version']['#access'] = FALSE; } // Otherwise, push the version string after the VCS label. else { $form['rel_id']['version']['#weight'] = 1; $form['rel_id']['version']['#value'] = $form['rel_id']['version']['#default_value']; $form['rel_id']['version']['#attributes']['disabled'] = 'disabled'; } // Hide the fieldset to view the version number parts. $form['project_release']['#access'] = FALSE; // Hide the API compatibility taxonomy selector. $vocab_id = _project_release_get_api_vid(); $form['taxonomy'][$vocab_id]['#access'] = FALSE; // Make sure the file upload UI is gone because users should *never* have a // way to upload files. However, we can still show attached files if they // already exist. // TODO (feature): it'd be nice if this was optional, so that some // sites might want to still allow file attachments for releases... $file = db_fetch_object(db_query("SELECT filepath FROM {files} f INNER JOIN {project_release_file} prf ON f.fid = prf.fid WHERE prf.nid = %d", $release_node->nid)); if (!$file || empty($file->filepath)) { unset($form['project_release_files']); } else { foreach (element_children($form['project_release_files']) as $key) { $form['project_release_files'][$key]['delete']['#access'] = FALSE; } } } function versioncontrol_release_project_release_form_edit_submit($form, &$form_state) { $release_node = $form['#node']; $project_node = node_load($release_node->project_release['pid']); db_query("UPDATE {versioncontrol_release_labels} SET label_id = %d WHERE release_nid = %d AND project_nid = %d", $form_state['values']['vcs_label'], $release_node->nid, $project_node->nid); if (!db_affected_rows()) { db_query("INSERT INTO {versioncontrol_release_labels} (release_nid, label_id, project_nid) VALUES (%d, %d, %d)", $release_node->nid, $form_state['values']['vcs_label'], $project_node->nid); } $repo = $project_node->versioncontrol_project['repo']; $branches = $repo->loadBranches(array($form_state['values']['vcs_label'])); $branch = reset($branches); db_query("UPDATE {project_release_nodes} SET tag = '%s' WHERE nid = %d", $branch->name, $release_node->nid); }