'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);
}