| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: release_node_form.inc,v 1.11 2010/01/30 02:33:40 dww Exp $ 3 4 /** 5 * @file 6 * Code required for the release node form. 7 */ 8 9 /** 10 * Private helper to implement hook_form(). 11 * 12 * Create the project release node form. 13 */ 14 function _project_release_form(&$release, &$form_state) { 15 if (arg(1) == 'add') { 16 // Initialize variables and $release object properties to prevent notices. 17 $is_edit = FALSE; 18 $admin = NULL; 19 $pid = (integer) arg(3); 20 $project = node_load($pid); 21 if (!isset($project) || $project->type != 'project_project') { 22 drupal_set_message(t('Node %nid is not a valid project.', array('%nid' => $pid)), 'error'); 23 drupal_goto('node/add/project-release'); 24 } 25 // Make sure this user should have permissions to add releases for 26 // the requested project 27 if (!project_check_admin_access($project)) { 28 drupal_access_denied(); 29 module_invoke_all('exit'); 30 exit; 31 } 32 project_project_set_breadcrumb($project, TRUE); 33 $format = project_release_get_version_format($project); 34 $release->project_release['pid'] = $pid; 35 } 36 else { 37 global $user; 38 $admin = user_access('administer projects'); 39 $is_edit = TRUE; 40 $project = node_load($release->project_release['pid']); 41 $breadcrumb[] = l($project->title, 'node/'. $project->nid); 42 $breadcrumb[] = l(t('Releases'), 'node/'. $project->nid . '/release'); 43 project_project_set_breadcrumb($project, $breadcrumb); 44 $format = project_release_get_version_format($project); 45 } 46 $form['project'] = array( 47 '#type' => 'value', 48 '#value' => $project, 49 ); 50 51 $modify = $admin || !$is_edit; 52 53 $form['#attributes'] = array("enctype" => "multipart/form-data"); 54 55 if ($is_edit) { 56 $form['rel_id'] = array( 57 '#type' => 'fieldset', 58 '#title' => t('Release identification'), 59 '#weight' => -4, 60 '#collapsible' => TRUE, 61 '#theme' => 'project_release_node_form_version_elements', 62 ); 63 } 64 _project_release_form_add_text_element($form['rel_id']['title'], t('Title'), $release->title, $is_edit, $admin, TRUE, 40, 128); 65 if (!empty($release->project_release['version'])) { 66 _project_release_form_add_text_element($form['rel_id']['version'], t('Version string'), $release->project_release['version'], $is_edit, $admin, TRUE, 20, 255); 67 } 68 // The version string belongs in $node->project_release[], so we set 69 // #parents to ensure the form value is placed there during validate, 70 // preview, and submit. 71 $form['rel_id']['version']['#parents'] = array('project_release', 'version'); 72 73 $form['project_release'] = array( 74 '#type' => 'fieldset', 75 '#tree' => TRUE, 76 '#title' => t('Version number elements'), 77 '#collapsible' => TRUE, 78 '#theme' => 'project_release_node_form_version_elements', 79 ); 80 81 if (!empty($release->project_release['pid'])) { 82 $pid = $release->project_release['pid']; 83 } 84 elseif (!empty($form_state['values']['project_release']['pid'])) { 85 $pid = $form_state['values']['project_release']['pid']; 86 } 87 // else? 88 $form['project_release']['pid'] = array( 89 '#type' => 'value', 90 '#value' => $pid, 91 ); 92 93 $form['validate_version'] = array('#type' => 'value', '#value' => 1); 94 _project_release_form_add_version_element($form, $release, $modify, $format, 'major', t('Major')); 95 _project_release_form_add_version_element($form, $release, $modify, $format, 'minor', t('Minor')); 96 _project_release_form_add_version_element($form, $release, $modify, $format, 'patch', t('Patch-level')); 97 _project_release_form_add_version_element($form, $release, $modify, $format, 'extra', t('Extra identifier'), t('Optionally specify other identifying information for this version, for example "beta-1", "rc-1" or "dev". In most cases, this should be left blank.'), 40); 98 99 // A boolean to indicate if this release should be regularly rebuilt 100 // (e.g. from a revision control branch) or not (e.g. an official release 101 // from a tag). There's no UI for this field, since it's only used if a 102 // revision-control integration module is enabled. 103 $form['project_release']['rebuild'] = array( 104 '#type' => 'value', 105 '#value' => !empty($release->project_release['rebuild']), 106 ); 107 108 $form['project_release_files'] = array( 109 '#type' => 'fieldset', 110 '#tree' => TRUE, 111 '#title' => t('File information'), 112 '#collapsible' => TRUE, 113 ); 114 115 if (!empty($release->project_release['files'])) { 116 $files = $release->project_release['files']; 117 foreach ($files as $fid => $file) { 118 // In the case of previews, we only have the fid of the file, 119 // so check for that and load the file object if necessary. 120 if (is_numeric($file)) { 121 $file = project_release_load_file($file); 122 } 123 $form['project_release_files'][$fid]['file_info'] = array( 124 '#value' => theme('project_release_download_file', $file, FALSE), 125 ); 126 if ($admin) { 127 $form['project_release_files'][$fid]['delete'] = array( 128 '#type' => 'checkbox', 129 '#title' => t('Delete @filename', array('@filename' => $file->filename)), 130 '#default_value' => isset($release->project_release_files[$fid]['delete']) ? $release->project_release_files[$fid]['delete'] : 0, 131 // TODO: we'll need to rip this out when multiple files lands. 132 '#description' => (isset($files) ? t('In order to add a new file, you must first delete %filename.', array('%filename' => $file->filename)) : ''), 133 ); 134 } 135 // Store the fid here in the proper $node->project_release[] array 136 // so it can be used to generate file previews. 137 $form['project_release']['files'][$fid] = array( 138 '#type' => 'value', 139 '#value' => $fid, 140 ); 141 } 142 } 143 144 // TODO: we'll need to rip out the conditional when multiple files lands. 145 if (!isset($files)) { 146 // In case of previews, get any uploaded file info. 147 if (isset($form_state['project_release']['new_file'])) { 148 $new_file = $form_state['project_release']['new_file']; 149 } 150 elseif (!empty($release->project_release_files['temp'])) { 151 $new_file = db_fetch_object(db_query("SELECT * FROM {files} WHERE fid = %d", $release->project_release_files['temp'])); 152 } 153 $form['project_release_files']['file'] = array( 154 '#title' => t('File'), 155 '#type' => 'file', 156 '#description' => isset($new_file) ? t('A file named %filename has already been uploaded. If you upload another file %filename will be replaced.', array('%filename' => $new_file->filename)) : t('Choose a file that will be associated with this release.'), 157 ); 158 // Account for already uploaded files being previewed. 159 if (isset($new_file)) { 160 $form['project_release_files']['temp'] = array( 161 '#type' => 'value', 162 '#value' => $new_file->fid, 163 ); 164 } 165 } 166 167 $form['body_field'] = node_body_field($release, t('Release notes'), !$admin); 168 // Add a description to the body field. 169 $form['body_field']['body']['#description'] = t('Enter a description of this release, such as a list of the major changes or updates.'); 170 171 $tag = isset($release->project_release['tag']) ? $release->project_release['tag'] : ''; 172 _project_release_form_add_text_element($form['tag']['tag'], t('Tag'), $tag, $is_edit, $admin, TRUE, 40, 255); 173 $form['tag']['tag']['#parents'] = array('project_release', 'tag'); 174 175 // Add a custom validation function. 176 $form['#validate'][] = 'project_release_node_form_validate'; 177 return $form; 178 } 179 180 /** 181 * Modifies the given $form array to add the appropriate form element 182 * for the requested version field. Since the 20+ lines of code in 183 * here have to be duplicated 6 times in project_release_form(), this 184 * function exists so we can reuse the code. 185 * @see project_release_form 186 * @ingroup project_release_internal 187 * 188 * @param $form Form array to modify 189 * @param $release Relase node the form is for 190 * @param $modify Boolean indicating if we should allow modifications 191 * @param $format Version format string for this project 192 * @param $name Name of this version element 193 * @param $title Translatable title of the form element 194 * @param $description Translatable description of the form element. 195 * @param $size Size of the form element 196 * @param $required Boolean for if the form element should be required 197 */ 198 function _project_release_form_add_version_element(&$form, $release, $modify, $format, $name, $title, $description = '', $size = 10, $required = FALSE) { 199 $var_name = 'version_'. $name; 200 $regexp = "@.*[!#%]$name.*@"; 201 if (preg_match($regexp, $format)) { 202 $form['project_release'][$var_name] = array( 203 '#type' => 'textfield', 204 '#title' => $title, 205 '#default_value' => isset($release->project_release[$var_name]) ? $release->project_release[$var_name] : '', 206 '#size' => $size, 207 '#maxlength' => $size + 10, 208 '#attributes' => array('style' => 'width:auto'), 209 ); 210 if ($required) { 211 // TODO: handle this more flexibly for sites not using CVS 212 // perhaps if the format variable is UPPERCASE it's required, 213 // and lowercase is optional or something crazy? 214 $form[$var_name]['#required'] = TRUE; 215 } 216 if ($description) { 217 $form['project_release'][$var_name]['#description'] = $description; 218 } 219 if (!$modify) { 220 $form['project_release'][$var_name]['#attributes'] = array('disabled' => 'disabled'); 221 $form['project_release'][$var_name]['#value'] = $release->project_release[$var_name]; 222 } 223 } 224 else { 225 $form['project_release'][$var_name] = array( 226 '#type' => 'value', 227 '#value' => isset($release->project_release[$var_name]) ? $release->project_release[$var_name] : '', 228 ); 229 } 230 } 231 232 /** 233 * Modifies the given $form array to add the appropriate form element 234 * for the desired text field. Since the 16+ lines of code in here 235 * have to be duplicated 5 times in project_release_form(), this 236 * function exists so we can reuse the code. 237 * @see project_release_form 238 * @ingroup project_release_internal 239 * 240 * @param $form 241 * Reference to form element to add. 242 * @param $title 243 * Translatable title of the form element. 244 * @param $value 245 * The value to use in the form. 246 * @param $is_edit 247 * Boolean indicating if we're editing or creating. 248 * @param $admin 249 * Boolean for if the edit is by a project administrator. 250 * @param $required 251 * Boolean for if the field should be required. 252 * @param $size 253 * Value to use for the '#size' property. 254 * @param $maxlength 255 * Value to use for the '#maxlength' property. 256 */ 257 function _project_release_form_add_text_element(&$form, $title, $value, $is_edit, $admin, $required = TRUE, $size = 40, $maxlength = 50) { 258 if ($is_edit && !empty($value)) { 259 $form = array( 260 '#type' => 'textfield', 261 '#title' => $title, 262 '#default_value' => $value, 263 '#required' => $required, 264 '#size' => $size, 265 '#maxlength' => $maxlength, 266 ); 267 if(!$admin) { 268 $form['#attributes']['disabled'] = 'disabled'; 269 $form['#value'] = $value; 270 } 271 } 272 else { 273 $form = array( 274 '#type' => 'value', 275 '#value' => $value, 276 ); 277 } 278 } 279 280 /** 281 * Private callback to validate a release node form. 282 * 283 * @see project_release_node_form_validate() 284 */ 285 function _project_release_node_form_validate(&$form, &$form_state) { 286 global $user; 287 $project_release = $form_state['values']['project_release']; 288 if (!empty($form_state['values']['validate_version'])) { 289 if (!isset($project_release['version_major']) && !isset($project_release['version_minor']) && 290 !isset($project_release['version_patch']) && 291 (!($project_release['version_extra']) || $project_release['version_extra'] === '')) { 292 form_set_error('project_release][version_major', t('You must fill in some version information.')); 293 // TODO: find a better form value to mark as the error? 294 } 295 foreach (array('version_major' => t('Major version number'), 'version_minor' => t('Minor version number')) as $field => $name) { 296 $val = $project_release[$field]; 297 if (isset($val) && $val !== '' && !is_numeric($val)) { 298 form_set_error("project_release][$field", t('%name must be a number.', array('%name' => $name))); 299 } 300 } 301 $val = $project_release['version_patch']; 302 if (isset($val) && $val !== '' && !is_numeric($val) && $val != 'x') { 303 form_set_error('project_release][version_patch', t("Patch-level version number must be numeric or the letter 'x'.")); 304 } 305 } 306 307 $validators = array( 308 'project_release_validate_file_extension' => array(), 309 ); 310 311 // For some reason, with the $form['project_release_files'] element 312 // #tree'd, the uploaded file shows up in 'project_release_files' 313 // and not it's sub-element, so we use that here. 314 if ($file = file_save_upload('project_release_files', $validators, file_directory_path() . '/project')) { 315 // We need the file object, so pass that into $form_state here. 316 $form_state['project_release']['new_file'] = $file; 317 } 318 319 if (project_release_get_api_taxonomy()) { 320 $vid = _project_release_get_api_vid(); 321 if (isset($form_state['values']['taxonomy'])) { 322 $tid = $form_state['values']['taxonomy'][$vid]; 323 } 324 elseif (isset($form_state['values'][$vid])) { 325 $tid = $form_state['values'][$vid]; 326 } 327 if (isset($tid) && is_numeric($tid)) { 328 $form_state['values']['project_release']['version_api_tid'] = $tid; 329 $project_release['version_api_tid'] = $tid; 330 } 331 } 332 333 // With cvs.module installed, this validation is already handled. 334 // We only want to do it here if we're *not* doing the N-page form... 335 if (!empty($form_state['values']['validate_version']) && !isset($form_state['values']['nid'])) { 336 $version = (object) $project_release; 337 if (project_release_exists($version)) { 338 // TODO: is there a better form element to mark with this error? 339 form_set_error('project_release][version_patch', t('This version already exists for this project.')); 340 } 341 } 342 343 // TODO: it'd be nice to automagically reset the version string and 344 // title based on changes to the version elements on an edit, but we 345 // have to be careful not to break the fancy N-page form when 346 // cvs_form_alter() is involved... 347 $project = isset($form_state['values']['project']) ? $form_state['values']['project'] : new stdClass; 348 349 if (isset($project->project['uri'])) { 350 $project_name = $project->project['uri']; 351 } 352 elseif (isset($project_release['pid'])) { 353 $project_name = project_get_uri_from_nid($form_state['values']['project_release']['pid']); 354 } 355 356 if (isset($form_state['values']['title'])) { 357 // TODO: Magic re-setting to "%project_name %version" ?? 358 } 359 elseif (isset($project_release['version']) && $project_release['version'] !== '') { 360 form_set_value($form['title'], $project_name .' '. $project_release['version'], $form_state); 361 } 362 elseif (!empty($project)) { 363 $version = project_release_get_version((object) $project_release, $project); 364 form_set_value(array('#parents' => array('project_release', 'version')), $version, $form_state); 365 $title = !empty($project_name) ? $project_name : $project->title; 366 form_set_value($form['title'], "$title $version", $form_state); 367 } 368 } 369 370 function project_release_validate_file_extension($file) { 371 // Make sure that the extension on the file is one of the allowed 372 // extensions for release files. Most of this validation code was 373 // modified from the code in file_check_upload(). 374 $extensions = variable_get('project_release_file_extensions', PROJECT_RELEASE_FILE_EXTENSIONS); 375 $regex = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i'; 376 if (!preg_match($regex, $file->filename)) { 377 return array(t('It is only possible to attach files with the following extensions: %files-allowed.', array('%files-allowed' => $extensions))); 378 } 379 return array(); 380 } 381 382 function project_release_node_submit(&$form, $form_state) { 383 // Get rid of the file upload item, not needed. 384 unset($form_state['values']['project_release_files']['file']); 385 // Look for newly uploaded files. 386 if (isset($form_state['project_release']['new_file'])) { 387 $new_file = $form_state['project_release']['new_file']; 388 } 389 elseif (!empty($form_state['values']['project_release_files']['temp'])) { 390 $temp = $form_state['values']['project_release_files']['temp']; 391 // Have to ensure the temp file hasn't been wiped from the files table. 392 if ($temp_file = db_fetch_object(db_query("SELECT * FROM {files} WHERE fid = %d", $temp))) { 393 $new_file = $temp_file; 394 } 395 unset($form_state['values']['project_release_files']['temp']); 396 } 397 $existing_files = !empty($form_state['values']['project_release_files']) ? $form_state['values']['project_release_files'] : NULL; 398 399 if (isset($existing_files)) { 400 foreach ($existing_files as $fid => $values) { 401 if ($values['delete']) { 402 $file = db_fetch_object(db_query("SELECT * FROM {files} WHERE fid = %d", $fid)); 403 project_release_file_delete($file); 404 } 405 } 406 } 407 // Add new files. 408 if (isset($new_file)) { 409 $status_updated = file_set_status($new_file, FILE_STATUS_PERMANENT); 410 if ($status_updated) { 411 $new_file->nid = $form_state['nid']; 412 $filepath = file_create_path($new_file->filepath); 413 $new_file->filehash = md5_file($filepath); 414 drupal_write_record('project_release_file', $new_file); 415 } 416 } 417 } 418 419 /** 420 * Helper method to take data out of a $node object and store it into 421 * the DB as necessary. Sadly, db_query() doesn't let us store NULL in 422 * the DB, since those get cast to 0. Therefore, we have to do some 423 * manual effort to dynamically create the appropriate SQL depending 424 * on which version fields are set in the release node. 425 * @see project_release_insert 426 * @see project_release_update 427 * @see db_query 428 * @ingroup project_release_internal 429 * 430 * @param $node 431 * Object containing form values from the project_release node form. Even 432 * though this is NOT a fully loaded $node object, the release-related 433 * values are in the $node->project_release array due to manual #tree and 434 * #parents hacking in project_release_form(). 435 * @param $is_new Is this a new release node, or are we updating? 436 */ 437 function project_release_db_save($node, $is_new) { 438 // If the patch field is set to a non-numeric value, we just want to 439 // keep it as a NULL in the DB, instead of casting it to a 0. 440 if (isset($node->project_release['version_patch']) && !is_numeric($node->project_release['version_patch'])) { 441 unset($node->project_release['version_patch']); 442 } 443 444 $types = array('pid' => "%d", 'version' => "'%s'", 'tag' => "'%s'", 445 'rebuild' => "%d", 446 ); 447 $values = array( 448 'pid' => $node->project_release['pid'], 449 'version' => $node->project_release['version'], 450 'tag' => $node->project_release['tag'], 451 'rebuild' => $node->project_release['rebuild'], 452 ); 453 $fields = array('version_major', 'version_minor', 'version_patch', 'version_api_tid'); 454 foreach ($fields as $field) { 455 if (isset($node->project_release[$field]) && is_numeric($node->project_release[$field])) { 456 $types[$field] = "%d"; 457 $values[$field] = $node->project_release[$field]; 458 } 459 } 460 if (module_exists('taxonomy')) { 461 // version_api_tid might not be where we think it is, so if we don't have 462 // a real value by now, look in $node->taxonomy 463 if (empty($types['version_api_tid'])) { 464 $vid = _project_release_get_api_vid(); 465 if (isset($node->taxonomy[$vid])) { 466 if (is_array($node->taxonomy[$vid])) { 467 $api_tid = reset($node->taxonomy[$vid]); 468 } 469 else { 470 $api_tid = (int)$node->taxonomy[$vid]; 471 } 472 $types['version_api_tid'] = '%d'; 473 $values['version_api_tid'] = $api_tid; 474 } 475 } 476 477 if (($type_vid = _project_release_get_release_type_vid()) && ($security_tid = variable_get('project_release_security_update_tid', 0))) { 478 $types['security_update'] = '%d'; 479 $values['security_update'] = !empty($node->taxonomy[$type_vid][$security_tid]); 480 } 481 } 482 483 $version_extra_weight_map = project_release_get_version_extra_weight_map(); 484 if (!empty($node->project_release['version_extra'])) { 485 $types['version_extra'] = "'%s'"; 486 $values['version_extra'] = $node->project_release['version_extra']; 487 // Since we have a version_extra defined, see what the weight should be, 488 // based on our current mapping of version_extra prefixes to weights. 489 foreach ($version_extra_weight_map as $prefix => $weight) { 490 // If the $prefix exists inside version_extra, we have a match. We use 491 // === 0 to tell the difference between the prefix being at position 0 492 // (start of the string) vs. strpos() returning FALSE (not found). 493 if (strpos($node->project_release['version_extra'], $prefix) === 0) { 494 $types['version_extra_weight'] = "%d"; 495 $values['version_extra_weight'] = $weight; 496 break; 497 } 498 } 499 // If version_extra contains any digits, save them as version_extra_delta. 500 // This is used to ensure that alpha10 is considered "newer" than alpha9. 501 $match = array(); 502 if (preg_match('/(\d+)/', $node->project_release['version_extra'], $match)) { 503 $types['version_extra_delta'] = "%d"; 504 $values['version_extra_delta'] = $match[1]; 505 } 506 } 507 // If there's no version_extra, but our mapping defines a weight for 'NULL', 508 // specify save that weight into the version_extra_weight column. 509 elseif (!empty($version_extra_weight_map['NULL'])) { 510 $types['version_extra_weight'] = "%d"; 511 $values['version_extra_weight'] = $version_extra_weight_map['NULL']; 512 } 513 514 if ($is_new) { 515 $types['nid'] = "%d"; 516 $sql = 'INSERT INTO {project_release_nodes} ('. implode(', ', array_keys($types)) .') VALUES ('. implode(', ', $types) .')'; 517 } 518 else { 519 $arr = array(); 520 foreach ($types as $key => $value) { 521 $arr[] = $key .' = '. $value; 522 } 523 $sql = 'UPDATE {project_release_nodes} SET '. implode(',', $arr) .' WHERE nid = %d'; 524 } 525 $values['nid'] = $node->nid; 526 db_query($sql, $values); 527 } 528 529 /** 530 * Redirect node/add/project_release/* to node/add/project-release/*. 531 */ 532 function project_release_add_redirect_page() { 533 $arg = arg(3); 534 drupal_goto('node/add/project-release/' . (empty($arg) ? '' : $arg)); 535 } 536 537 /** 538 * Form builder for a simple form to select a project when creating a new 539 * release (as the first "page", but this is not really a multi-page form). 540 */ 541 function project_release_pick_project_form() { 542 $form = array(); 543 544 drupal_set_title(t('Submit @name', array('@name' => node_get_types('name', 'project_release')))); 545 546 // Fetch a list of all projects. 547 $uris = NULL; 548 $projects = array(0 => t('- Select a project -')) + project_projects_select_options($uris); 549 if (count($projects) == 1) { 550 drupal_set_message(t('You do not have access to any projects.'), 'error'); 551 } 552 553 $form['pid'] = array( 554 '#type' => 'select', 555 '#title' => t('Project'), 556 '#options' => $projects, 557 '#required' => TRUE, 558 ); 559 $form['submit'] = array( 560 '#type' => 'submit', 561 '#value' => t('Next'), 562 ); 563 return $form; 564 } 565 566 function project_release_pick_project_form_validate($form, &$form_state) { 567 if (empty($form_state['values']['pid'])) { 568 form_set_error('pid', t('You must select a project.')); 569 } 570 $node = node_load($form_state['values']['pid']); 571 if (empty($node) || $node->type != 'project_project') { 572 form_set_error('pid', t('Invalid project selected.')); 573 } 574 } 575 576 function project_release_pick_project_form_submit($form, &$form_state) { 577 $form_state['redirect'] = 'node/add/project-release/'. $form_state['values']['pid']; 578 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |