| 1 |
<?php
|
| 2 |
// $Id: issue.inc,v 1.353 2009/06/18 03:27:57 dww Exp $
|
| 3 |
|
| 4 |
|
| 5 |
/**
|
| 6 |
* JS callback method to return updated elements on the issue form.
|
| 7 |
* This function is called when someone changes the "Project" selector.
|
| 8 |
*/
|
| 9 |
function project_issue_update_project() {
|
| 10 |
$form_state = array('storage' => NULL, 'submitted' => FALSE, 'rebuild' => TRUE);
|
| 11 |
$form_build_id = $_POST['form_build_id'];
|
| 12 |
$form = form_get_cache($form_build_id, $form_state);
|
| 13 |
$args = $form['#parameters'];
|
| 14 |
$form_id = array_shift($args);
|
| 15 |
$form_state['post'] = $form['#post'] = $_POST;
|
| 16 |
$form['#programmed'] = $form['#redirect'] = FALSE;
|
| 17 |
|
| 18 |
drupal_process_form($form_id, $form, $form_state);
|
| 19 |
|
| 20 |
// Rebuild the form and cache it again.
|
| 21 |
$form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
|
| 22 |
|
| 23 |
// These used to be path arguments. Now they arrive via form element.
|
| 24 |
$issue_nid = $form['nid']['#value'];
|
| 25 |
$pid = $form_state['values']['project_info']['pid'];
|
| 26 |
$rid = isset($form_state['values']['project_info']['rid']) ? $form_state['values']['project_info']['rid'] : 0;
|
| 27 |
$cid = $form_state['values']['cid'];
|
| 28 |
$assigned_uid = $form_state['values']['project_info']['assigned'];
|
| 29 |
|
| 30 |
$project_info = $form['original_issue']['project_info'];
|
| 31 |
|
| 32 |
// Only generate release stuff if the project_release module is enabled.
|
| 33 |
if (module_exists('project_release')) {
|
| 34 |
|
| 35 |
$project->nid = $pid;
|
| 36 |
if ($releases = project_release_get_releases($project, 0)) {
|
| 37 |
$old_version = db_result(db_query("SELECT version FROM {project_release_nodes} WHERE nid = %d", $rid));
|
| 38 |
$releases = array(t('<none>')) + $releases;
|
| 39 |
// Since the value of releases is by nid, try to match release
|
| 40 |
// based on the label instead.
|
| 41 |
$default = 0;
|
| 42 |
foreach ($releases as $key => $label) {
|
| 43 |
if ($old_version == $label) {
|
| 44 |
$default = $key;
|
| 45 |
}
|
| 46 |
}
|
| 47 |
// Element is tree'd here to match the original form layout.
|
| 48 |
$project_info['#tree'] = TRUE;
|
| 49 |
$project_info['rid']['#options'] = $releases;
|
| 50 |
$project_info['rid']['#disabled'] = FALSE;
|
| 51 |
if (!empty($default)) {
|
| 52 |
$project_info['rid']['#value'] = $default;
|
| 53 |
$project_info['rid']['#default_value'] = $default;
|
| 54 |
}
|
| 55 |
}
|
| 56 |
else {
|
| 57 |
$project_info['rid']['#disabled'] = TRUE;
|
| 58 |
$project_info['rid']['#options'] = array();
|
| 59 |
}
|
| 60 |
}
|
| 61 |
|
| 62 |
// Assigned.
|
| 63 |
if (is_numeric($issue_nid)) {
|
| 64 |
$issue = node_load(array('nid' => $issue_nid, 'type' => 'project_issue'));
|
| 65 |
}
|
| 66 |
else {
|
| 67 |
// @TODO: This case should not ever be hit until
|
| 68 |
// http://drupal.org/node/197281 lands.
|
| 69 |
$issue = new stdClass;
|
| 70 |
}
|
| 71 |
$issue->project_issue['pid'] = $pid;
|
| 72 |
$assigned_choices = project_issue_assigned_choices($issue);
|
| 73 |
|
| 74 |
$project_info['assigned']['#default_value'] = $assigned_uid;
|
| 75 |
$project_info['assigned']['#options'] = $assigned_choices;
|
| 76 |
|
| 77 |
// Components.
|
| 78 |
$project = db_fetch_object(db_query('SELECT * FROM {project_issue_projects} WHERE nid = %d', $pid));
|
| 79 |
$components = array();
|
| 80 |
if ($project->components) {
|
| 81 |
$components = array(t('<none>'));
|
| 82 |
foreach (unserialize($project->components) as $component) {
|
| 83 |
$component = check_plain($component);
|
| 84 |
$components[$component] = $component;
|
| 85 |
}
|
| 86 |
}
|
| 87 |
|
| 88 |
$project_info['component']['#default_value'] = $cid;
|
| 89 |
$project_info['component']['#options'] = $components;
|
| 90 |
|
| 91 |
// Build the HTML output for the component select.
|
| 92 |
$output = theme('status_messages') . drupal_render($project_info);
|
| 93 |
drupal_json(array('status' => TRUE, 'data' => $output));
|
| 94 |
exit;
|
| 95 |
}
|
| 96 |
|
| 97 |
/**
|
| 98 |
* Build an array of users to whom an issue may be assigned.
|
| 99 |
*
|
| 100 |
* @param $issue
|
| 101 |
* The fully loaded issue node object.
|
| 102 |
* @return
|
| 103 |
* A keyed array in the form uid => name containing users
|
| 104 |
* to whom an issue may be assigned.
|
| 105 |
*/
|
| 106 |
function project_issue_assigned_choices($issue) {
|
| 107 |
// Setup the array of choices for who the issue is assigned to.
|
| 108 |
$assigned = array();
|
| 109 |
foreach (module_implements('project_issue_assignees') as $module) {
|
| 110 |
$function = "{$module}_project_issue_assignees";
|
| 111 |
$function($assigned, $issue);
|
| 112 |
}
|
| 113 |
natcasesort($assigned);
|
| 114 |
$assigned = array(0 => empty($issue->project_issue['assigned']) ? t('Unassigned') : t('Unassign')) + $assigned;
|
| 115 |
return $assigned;
|
| 116 |
}
|
| 117 |
|
| 118 |
/**
|
| 119 |
* Implementation of hook_project_issue_assignees().
|
| 120 |
*
|
| 121 |
* This hook is used to modify the list of 'Assigned' users when creating or
|
| 122 |
* commenting on an issue.
|
| 123 |
*
|
| 124 |
* @param $assigned
|
| 125 |
* An array of key => value pairs in the format of uid => name that represents
|
| 126 |
* the list of users that the issue may be assigned to. This parameter
|
| 127 |
* is passed by reference so the hook may make changes, such as adding or
|
| 128 |
* removing elements.
|
| 129 |
* @param $node
|
| 130 |
* The node object for the issue.
|
| 131 |
*/
|
| 132 |
function project_issue_project_issue_assignees(&$assigned, $node) {
|
| 133 |
global $user;
|
| 134 |
if ($user->uid) {
|
| 135 |
if (isset($node->project_issue['assigned']) && $user->uid != $node->project_issue['assigned']) {
|
| 136 |
// Assigned to someone else, add the currently assigned user.
|
| 137 |
$account = user_load(array('uid' => $node->project_issue['assigned']));
|
| 138 |
$assigned[$node->project_issue['assigned']] = $account->name;
|
| 139 |
}
|
| 140 |
// Always let the person replying assign it to themselves.
|
| 141 |
$assigned[$user->uid] = $user->name;
|
| 142 |
}
|
| 143 |
|
| 144 |
if (user_access('assign and be assigned project issues')) {
|
| 145 |
// All users are included if either anon or auth user has the perm.
|
| 146 |
if (db_result(db_query("SELECT rid FROM {permission} WHERE perm LIKE '%%%s%%' AND rid IN(%d, %d)", 'assign and be assigned project issues', DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
|
| 147 |
$result = db_query("SELECT uid, name FROM {users}");
|
| 148 |
}
|
| 149 |
else {
|
| 150 |
$result = db_query("SELECT u.uid, u.name FROM {users} u INNER JOIN {users_roles} ur ON u.uid = ur.uid INNER JOIN {role} r ON ur.rid = r.rid INNER JOIN {permission} p ON p.rid = r.rid WHERE p.perm LIKE '%%%s%%'", 'assign and be assigned project issues');
|
| 151 |
}
|
| 152 |
|
| 153 |
while ($assignee = db_fetch_object($result)) {
|
| 154 |
$assigned[$assignee->uid] = $assignee->name;
|
| 155 |
}
|
| 156 |
}
|
| 157 |
}
|
| 158 |
|
| 159 |
// Support stuff
|
| 160 |
|
| 161 |
/**
|
| 162 |
* Return information about project issue state values.
|
| 163 |
*
|
| 164 |
* @param $sid
|
| 165 |
* Integer state id to return information about, or 0 for all states.
|
| 166 |
* @param $restrict
|
| 167 |
* Boolean to determine if states should be restricted based on the
|
| 168 |
* permissions of the current user or $account if that parameter
|
| 169 |
* is provided.
|
| 170 |
* @param $is_author
|
| 171 |
* Boolean that indicates if the current user is the author of the
|
| 172 |
* issue, which can potentially grant a wider selection of states.
|
| 173 |
* @param $current_sid
|
| 174 |
* The current integer state id for the issue.
|
| 175 |
* @param $defaults
|
| 176 |
* Boolean to request the states used for default issue queries.
|
| 177 |
* @param $account
|
| 178 |
* Account of a user to pass to user_access() for access checking.
|
| 179 |
* This parameter will have no effect unless $restrict is also
|
| 180 |
* set to TRUE.
|
| 181 |
*
|
| 182 |
* @return
|
| 183 |
* An array of states (sid as key, name as value) that match the
|
| 184 |
* given filters, or the name of the requested state.
|
| 185 |
* NOTE: The state name returned is raw text, and should be filtered via
|
| 186 |
* check_plain() or filter_xss() before being printed as HTML output.
|
| 187 |
*/
|
| 188 |
function project_issue_state($sid = 0, $restrict = false, $is_author = false, $current_sid = 0, $defaults = false, $account = NULL) {
|
| 189 |
static $options;
|
| 190 |
|
| 191 |
if (!$options) {
|
| 192 |
$result = db_query('SELECT * FROM {project_issue_state} ORDER BY weight');
|
| 193 |
while ($state = db_fetch_object($result)) {
|
| 194 |
$options[] = $state;
|
| 195 |
}
|
| 196 |
}
|
| 197 |
|
| 198 |
foreach($options as $state) {
|
| 199 |
if ($restrict) {
|
| 200 |
// Check if user has access,
|
| 201 |
// or if status is default status and therefore available to all,
|
| 202 |
// or if user is original issue author and author has access,
|
| 203 |
// or if the issue is already in a state, even if the user doesn't have
|
| 204 |
// access to that state themselves.
|
| 205 |
if (user_access('set issue status '. str_replace("'", "", $state->name), $account)
|
| 206 |
|| user_access('administer projects', $account)
|
| 207 |
|| ($state->sid == variable_get('project_issue_default_state', 1))
|
| 208 |
|| ($state->author_has && $is_author)
|
| 209 |
|| ($state->sid == $current_sid) ) {
|
| 210 |
$states[$state->sid] = $state->name;
|
| 211 |
}
|
| 212 |
}
|
| 213 |
else if ($defaults) {
|
| 214 |
if ($state->default_query) {
|
| 215 |
$states[$state->sid] = $state->name;
|
| 216 |
}
|
| 217 |
}
|
| 218 |
else {
|
| 219 |
$states[$state->sid] = $state->name;
|
| 220 |
}
|
| 221 |
}
|
| 222 |
|
| 223 |
return $sid ? $states[$sid] : $states;
|
| 224 |
}
|
| 225 |
|
| 226 |
/**
|
| 227 |
* Return an array of state ids that should be used for default queries.
|
| 228 |
*/
|
| 229 |
function project_issue_default_states() {
|
| 230 |
static $defaults;
|
| 231 |
if (empty($defaults)) {
|
| 232 |
$states = project_issue_state(0, false, false, 0, true);
|
| 233 |
$defaults = !empty($states) ? array_keys($states) : array(1, 2, 4);
|
| 234 |
}
|
| 235 |
return $defaults;
|
| 236 |
}
|
| 237 |
|
| 238 |
function project_issue_priority($priority = 0) {
|
| 239 |
$priorities = array(1 => t('critical'), t('normal'), t('minor'));
|
| 240 |
return $priority ? $priorities[$priority] : $priorities;
|
| 241 |
}
|
| 242 |
|
| 243 |
function project_issue_category($category = 0, $plural = 1) {
|
| 244 |
if ($plural) {
|
| 245 |
$categories = array('bug' => t('bug reports'), 'task' => t('tasks'), 'feature' => t('feature requests'), 'support' => t('support requests'));
|
| 246 |
}
|
| 247 |
else {
|
| 248 |
$categories = array('bug' => t('bug report'), 'task' => t('task'), 'feature' => t('feature request'), 'support' => t('support request'));
|
| 249 |
}
|
| 250 |
return $category ? $categories[$category] : $categories;
|
| 251 |
}
|
| 252 |
|
| 253 |
function project_issue_count($pid) {
|
| 254 |
$state = array();
|
| 255 |
$result = db_query('SELECT p.sid, count(p.sid) AS count FROM {node} n INNER JOIN {project_issues} p ON n.nid = p.nid WHERE n.status = 1 AND p.pid = %d GROUP BY p.sid', $pid);
|
| 256 |
while ($data = db_fetch_object($result)) {
|
| 257 |
$state[$data->sid] = $data->count;
|
| 258 |
}
|
| 259 |
return $state;
|
| 260 |
}
|
| 261 |
|
| 262 |
/**
|
| 263 |
* Return the HTML to use for the links at the top of issue query pages.
|
| 264 |
*
|
| 265 |
* @param $links
|
| 266 |
* Array of links, indexed by the action ('create', 'search', etc).
|
| 267 |
*
|
| 268 |
* @see project_issue_query_result()
|
| 269 |
* @see theme_links()
|
| 270 |
*/
|
| 271 |
function theme_project_issue_query_result_links($links) {
|
| 272 |
return theme('links', $links);
|
| 273 |
}
|
| 274 |
|
| 275 |
/**
|
| 276 |
* Provide an array of project issue summary fields.
|
| 277 |
* @param $context
|
| 278 |
* The context in which the fields will be displayed.
|
| 279 |
* 'web' for project_issue node and comment summary tables.
|
| 280 |
* 'email' for project_issue notification e-mail messages.
|
| 281 |
*/
|
| 282 |
function project_issue_field_labels($context) {
|
| 283 |
|
| 284 |
if ($context == 'web') {
|
| 285 |
$labels = array('title' => t('Title'));
|
| 286 |
}
|
| 287 |
else {
|
| 288 |
$labels = array();
|
| 289 |
}
|
| 290 |
|
| 291 |
$labels += array(
|
| 292 |
'pid' => t('Project'),
|
| 293 |
'rid' => t('Version'),
|
| 294 |
'component' => t('Component'),
|
| 295 |
'category' => t('Category'),
|
| 296 |
'priority' => t('Priority'),
|
| 297 |
'assigned' => t('Assigned to'),
|
| 298 |
'sid' => t('Status'),
|
| 299 |
);
|
| 300 |
|
| 301 |
if ($context == 'email') {
|
| 302 |
$labels += array(
|
| 303 |
'name' => t('Reported by'),
|
| 304 |
'updator' => t('Updated by'),
|
| 305 |
);
|
| 306 |
}
|
| 307 |
return $labels;
|
| 308 |
}
|
| 309 |
|
| 310 |
/**
|
| 311 |
* Provide the text displayed to a user for a specific metadata field.
|
| 312 |
*
|
| 313 |
* @param $field
|
| 314 |
* Name of the field.
|
| 315 |
* @param $value
|
| 316 |
* Value of the field.
|
| 317 |
*
|
| 318 |
* @return
|
| 319 |
* Text to display to user. NOTE: This is unfiltered text! If this output is
|
| 320 |
* being sent over the web, you must use check_plain().
|
| 321 |
*/
|
| 322 |
function project_issue_change_summary($field, $value) {
|
| 323 |
switch ($field) {
|
| 324 |
case 'pid':
|
| 325 |
$project = node_load(array('nid' => $value, 'type' => 'project_project'));
|
| 326 |
return $project->title;
|
| 327 |
case 'category':
|
| 328 |
return $value ? project_issue_category($value, 0) : t('<none>');
|
| 329 |
case 'priority':
|
| 330 |
return $value ? project_issue_priority($value) : t('<none>');
|
| 331 |
case 'rid':
|
| 332 |
if ($value) {
|
| 333 |
$release->nid = $value;
|
| 334 |
if (module_exists('project_release')) {
|
| 335 |
$release = project_release_load($release);
|
| 336 |
}
|
| 337 |
else {
|
| 338 |
$release->project_release['version'] = t('Unknown');
|
| 339 |
}
|
| 340 |
return $release->project_release['version'];
|
| 341 |
}
|
| 342 |
return t('<none>');
|
| 343 |
case 'assigned':
|
| 344 |
$user = user_load(array('uid' => $value));
|
| 345 |
return (int) $value === 0 ? variable_get('anonymous', t('Anonymous')) : $user->name;
|
| 346 |
case 'sid':
|
| 347 |
return $value ? project_issue_state($value) : t('<none>');
|
| 348 |
default:
|
| 349 |
return $value;
|
| 350 |
}
|
| 351 |
}
|
| 352 |
|
| 353 |
function theme_project_issue_create_forbidden($uri = '') {
|
| 354 |
global $user;
|
| 355 |
if ($user->uid) {
|
| 356 |
return '';
|
| 357 |
}
|
| 358 |
|
| 359 |
// We cannot use drupal_get_destination() because these links sometimes appear on /node and taxo listing pages.
|
| 360 |
$destination = "destination=". drupal_urlencode("node/add/project-issue/$uri");
|
| 361 |
|
| 362 |
if (variable_get('user_register', 1)) {
|
| 363 |
return t('<a href="@login">Login</a> or <a href="@register">register</a> to create an issue', array('@login' => url('user/login', array('query' => $destination)), '@register' => url('user/register', array('query' => $destination))));
|
| 364 |
}
|
| 365 |
else {
|
| 366 |
return t('<a href="@login">Login</a> to create an issue', array('@login' => url('user/login', array('query' => $destination))));
|
| 367 |
}
|
| 368 |
}
|