/[drupal]/contributions/modules/project/project.inc
ViewVC logotype

Contents of /contributions/modules/project/project.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.146 - (show annotations) (download) (as text)
Mon Aug 10 22:50:39 2009 UTC (3 months, 2 weeks ago) by dww
Branch: MAIN
Changes since 1.145: +4 -4 lines
File MIME type: text/x-php
#129664 by nezroy, agentrickard, aclight, dww: Added a setting to disable
automatic URL aliases for project nodes (e.g. to use pathauto instead).
1 <?php
2 // $Id: project.inc,v 1.145 2009/06/15 03:51:00 dww Exp $
3
4 /**
5 * Implementation of hook_form().
6 */
7 function project_project_form($node, $form_state) {
8 global $user;
9 project_project_set_breadcrumb($node);
10
11 /* Project taxonomy */
12 if (project_use_taxonomy()) {
13 // Since this form is used relatively infrequently, don't allow the js to be aggregated.
14 drupal_add_js(drupal_get_path('module', 'project') .'/project.js', 'module', 'header', FALSE, TRUE, FALSE);
15 $project_type_vid = _project_get_vid();
16 $tree = taxonomy_get_tree($project_type_vid);
17 $top_level = array();
18 $options = array();
19 foreach ($tree as $i => $term) {
20 if ($term->parents[0] == 0) {
21 $last_top = $term->tid;
22 $top_level[$term->tid] = check_plain($term->name);
23 }
24 else {
25 $options[$last_top][$term->tid] = $term->name;
26 }
27 }
28 // See if there are any project specific taxonomy terms already
29 // saved in this node (i.e. we're editing an existing project) and
30 // if so, extract the right default values for our custom form
31 // elements...
32 $current_top = NULL;
33 $current_options = array();
34
35 if (!empty($node->taxonomy)) {
36 // Depending on whether we're previewing the node or not,
37 // $node->taxonomy will be provided in one of two ways.
38 if (isset($form_state['node_preview'])) {
39 // In node previews, $node->taxonomy is an array of vocabularies,
40 // each of which is an array of selected tids.
41 if (isset($node->taxonomy[$project_type_vid])) {
42 foreach ($node->taxonomy[$project_type_vid] as $key => $value) {
43 if (isset($top_level[$key])) {
44 $current_top = $key;
45 }
46 else {
47 $current_options[$key] = $key;
48 }
49 }
50 }
51 }
52 else {
53 // $node->taxonomy is an array of term objects
54 // when we're not previewing the node.
55 foreach ($node->taxonomy as $tid => $obj) {
56 if (isset($top_level[$tid])) {
57 $current_top = $tid;
58 }
59 else {
60 $current_options[$tid] = $tid;
61 }
62 }
63 }
64 }
65
66 $form['project_taxonomy'] = array(
67 '#type' => 'fieldset',
68 '#weight' => '-30',
69 '#title' => t('Project categories'),
70 '#collapsible' => TRUE,
71 '#theme' => 'project_project_node_form_taxonomy',
72 );
73
74 $form['project_taxonomy']['project_type'] = array(
75 '#title' => t('Project type'),
76 '#type' => 'radios',
77 '#options' => $top_level,
78 '#default_value' => $current_top,
79 '#required' => TRUE,
80 );
81 $select_size = max(5, 2*count($top_level));
82 foreach ($options as $tid => $values) {
83 $form['project_taxonomy']["tid_$tid"] = array(
84 '#title' => t('!type categories', array('!type' => $top_level[$tid])),
85 '#type' => 'select',
86 '#multiple' => TRUE,
87 '#options' => $values,
88 '#default_value' => $current_options,
89 '#attributes' => array('size' => min($select_size, count($values))),
90 );
91 }
92 }
93
94 /* Project properties */
95 // We can't put the title and body inside $node->project or core gets
96 // confused (e.g node_body_field() and friends). So, we put the core node
97 // fields in their own fieldset (for which is #tree is FALSE).
98 $form['project_node'] = array(
99 '#type' => 'fieldset',
100 '#title' => t('Project information'),
101 '#collapsible' => TRUE,
102 );
103 $form['project_node']['title'] = array(
104 '#type' => 'textfield',
105 '#title' => t('Full project name'),
106 '#default_value' => isset($node->title) ? $node->title : NULL,
107 '#maxlength' => 128,
108 '#required' => TRUE,
109 );
110 // This is sort of a hack: We want the 'uri' to be in the $node->project
111 // array during validation and submission of this form, to protect the $node
112 // namespace, even though from a usability standpoint, this field belongs
113 // right up next to the title. So, we add a 'project' subarray in here
114 // for which #tree is TRUE, and put the 'uri' field in there. That way, it
115 // still lives inside the "Project information" fieldset as far as the UI is
116 // concerned, but the value shows up in the $node->project array for
117 // validation and submission as far as FAPI is concerned.
118 $form['project_node']['project'] = array('#tree' => TRUE);
119 $form['project_node']['project']['uri'] = array(
120 '#type' => 'textfield',
121 '#title' => t('Short project name'),
122 '#default_value' => isset($node->project['uri']) ? $node->project['uri'] : NULL,
123 '#size' => 40,
124 '#maxlength' => 50,
125 '#description' => (variable_get('project_enable_alias', TRUE)) ? t('This will be used to generate a /project/&lt;shortname&gt;/ URL for your project.') : '',
126 '#required' => TRUE,
127 );
128 $form['project_node']['body_field'] = node_body_field($node, t('Full description'), 1);
129
130 $form['project'] = array(
131 '#type' => 'fieldset',
132 '#title' => t('Project resources'),
133 '#collapsible' => TRUE,
134 '#collapsed' => TRUE,
135 '#tree' => TRUE,
136 );
137 $form['project']['homepage'] = array(
138 '#type' => 'textfield',
139 '#title' => t('Homepage'),
140 '#default_value' => isset($node->project['homepage']) ? $node->project['homepage'] : NULL,
141 '#size' => 40,
142 '#maxlength' => 255,
143 '#description' => t('Link to project homepage.'),
144 );
145 $form['project']['documentation'] = array(
146 '#type' => 'textfield',
147 '#title' => t('Documentation'),
148 '#default_value' => isset($node->project['documentation']) ? $node->project['documentation'] : NULL,
149 '#size' => 40,
150 '#maxlength' => 255,
151 '#description' => t('Link to project documentation.'),
152 );
153 $form['project']['license'] = array(
154 '#type' => 'textfield',
155 '#title' => t('License'),
156 '#default_value' => isset($node->project['license']) ? $node->project['license'] : NULL,
157 '#size' => 40,
158 '#maxlength' => 255,
159 '#description' => t('Link to project license.'),
160 );
161 $form['project']['screenshots'] = array(
162 '#type' => 'textfield',
163 '#title' => t('Screenshots'),
164 '#default_value' => isset($node->project['screenshots']) ? $node->project['screenshots'] : NULL,
165 '#size' => 40,
166 '#maxlength' => 255,
167 '#description' => t('Link to project screenshots.'),
168 );
169 $form['project']['changelog'] = array(
170 '#type' => 'textfield',
171 '#title' => t('Changelog'),
172 '#default_value' => isset($node->project['changelog']) ? $node->project['changelog'] : NULL,
173 '#size' => 40,
174 '#maxlength' => 255,
175 '#description' => t('Link to changelog.'),
176 );
177 $form['project']['cvs'] = array(
178 '#type' => 'textfield',
179 '#title' => t('CVS tree'),
180 '#default_value' => isset($node->project['cvs']) ? $node->project['cvs'] : NULL,
181 '#size' => 40,
182 '#maxlength' => 255,
183 '#description' => t('Link to webcvs/viewcvs.'),
184 );
185 $form['project']['demo'] = array(
186 '#type' => 'textfield',
187 '#title' => t('Demo site'),
188 '#default_value' => isset($node->project['demo']) ? $node->project['demo'] : NULL,
189 '#size' => 40,
190 '#maxlength' => 255,
191 '#description' => t('Link to a live demo.'),
192 );
193
194 return $form;
195 }
196
197 /**
198 * Implementation of hook_validate().
199 *
200 * @param $node
201 * An object containing values from the project node form. Note that since
202 * this isn't a fully-loaded $node object, not all values will necessarily
203 * be in the same location as they would after a node_load().
204 */
205 function project_project_validate(&$node) {
206 // Bail if user hasn't done a preview yet.
207 if (!isset($node->title)) {
208 return $node;
209 }
210
211 // Make sure title isn't already in use
212 if (db_result(db_query("SELECT COUNT(*) FROM {node} WHERE type = '%s' AND status = %d AND title = '%s' AND nid <> %d", $node->type, 1, $node->title, $node->nid))) {
213 form_set_error('title', t('This project name is already in use.'));
214 }
215
216 // Validate uri.
217 if (empty($node->project['uri'])) {
218 form_set_error('project][uri', t('A short project name is required.'));
219 }
220 else {
221 // Make sure uri only includes valid characters
222 if (!preg_match('/^[a-zA-Z0-9_-]+$/', $node->project['uri'])) {
223 form_set_error('project][uri', t('Please only use alphanumerical characters for the project name.'));
224 }
225
226 // Make sure uri isn't already in use, or reserved. Includes all X from
227 // project/issues/X paths used in project_issues module
228 $reserved_names = array('user', 'issues', 'releases', 'rss', 'subscribe-mail', 'search', 'add', 'update_project', 'statistics', 'comments', 'autocomplete', 'cvs', 'developers', 'usage');
229 if (project_use_taxonomy()) {
230 $terms = taxonomy_get_tree(_project_get_vid());
231 foreach ($terms as $i => $term) {
232 if ($term->depth == 0) {
233 $reserved_names[] = strtolower($term->name);
234 }
235 }
236 }
237 if (in_array(strtolower($node->project['uri']), $reserved_names)) {
238 form_set_error('project][uri', t('This project name is reserved.'));
239 }
240 $existing_nid = project_get_nid_from_uri($node->project['uri']);
241 if (!empty($existing_nid) && $existing_nid != $node->nid) {
242 form_set_error('project][uri', t('This project name is already in use.'));
243 }
244 }
245
246 // Make sure all URL fields actually contain URLs.
247 $fields = array(
248 'homepage' => t('Homepage'),
249 'changelog' => t('Changelog'),
250 'cvs' => t('CVS tree'),
251 'demo' => t('Demo site'),
252 );
253 foreach ($fields as $uri => $name) {
254 if ($node->project[$uri] && !preg_match('/^(http|https|ftp):\/\//i', $node->project[$uri])) {
255 form_set_error("project][$uri", t('The %field field is not a valid URL.', array('%field' => $name)));
256 }
257 }
258
259 // Validate the project-specific sub-categories, if any...
260 if (project_use_taxonomy() && isset($node->project_type)) {
261 $tree = taxonomy_get_tree(_project_get_vid());
262 $top_level = array();
263 foreach ($tree as $i => $term) {
264 if ($term->parents[0] == 0) {
265 $top_level[$term->tid] = $term->name;
266 }
267 }
268 foreach ($top_level as $tid => $name) {
269 if ($node->project_type != $tid) {
270 $tid_field = 'tid_' . $tid;
271 if (!empty($node->$tid_field)) {
272 form_set_error($tid, t('Project type %project_type was selected, you can not use values from %invalid_type categories', array('%project_type' => $top_level[$node->project_type], '%invalid_type' => $top_level[$tid])));
273 }
274 }
275 }
276 }
277 }
278
279 function project_project_set_breadcrumb($node = NULL, $extra = NULL) {
280 $breadcrumb = array();
281 $breadcrumb[] = l(t('Home'), NULL);
282
283 /*
284 @TODO: This is not an optimal way to do this, because it makes the
285 assumption that there is only one menu item that links to /project (or
286 whatever it happens to be called). Also, it makes the assumption that the
287 URL alias is intact for the project node (in other words, it's path is
288 aliased to 'project/<project short name>' However, since in D6 we no
289 longer have $_menu I'm not sure of a better way to do this.
290 */
291 if (!empty($node->path)) {
292 // Get the path of the project node and remove the name of the project.
293 $path = array();
294 $path = explode('/', $node->path);
295 $path = array_slice($path, 0, count($path) - 1);
296 $path = implode('/', $path);
297 $menu_link = db_fetch_object(db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.hidden = %d AND ml.link_path = '%s' ORDER BY ml.weight", 0, $path));
298 if (!empty($menu_link)) {
299 $breadcrumb[] = l($menu_link->link_title, 'project', array('title' => t('Browse projects')));
300 }
301 }
302
303 if (!empty($node->nid) && project_use_taxonomy()) {
304 $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid INNER JOIN {term_node} r ON t.tid = r.tid WHERE h.parent = %d AND t.vid = %d AND r.vid = %d', 't', 'tid'), 0, _project_get_vid(), $node->vid);
305 $term = db_fetch_object($result);
306 $breadcrumb[] = l($term->name, 'project/'. $term->name);
307 }
308
309 if (is_array($extra)) {
310 $breadcrumb = array_merge($breadcrumb, $extra);
311 }
312 elseif ($extra && !empty($node)) {
313 $breadcrumb[] = l($node->title, 'node/'. $node->nid);
314 }
315
316 drupal_set_breadcrumb($breadcrumb);
317 }
318
319 function project_project_view($node, $teaser = false, $page = false) {
320 $node = node_prepare($node, $teaser);
321
322 if ($page) {
323 // Breadcrumb navigation
324 project_project_set_breadcrumb($node);
325
326 // If theme_project_release_project_download_table is implemented, format
327 // the download table. If this function is not implemented (eg. if the
328 // project_release module is not enabled), there will not be an error
329 // but of course there will be no release table.
330 $project_table_output = theme('project_release_project_download_table', $node);
331 if (!empty($project_table_output)) {
332 $node->content['download_table'] = array(
333 '#value' => $project_table_output,
334 '#weight' => 1,
335 );
336 }
337
338 // Retrieve nested array of sections of links to display on node page.
339 $all_links = project_get_project_link_info($node);
340
341 // Format links in $all_links for display in the project_project node.
342 // Keep track of the section with the heaviest weight (which will be last)
343 // so we can add a final clear after it to make sure the floating link
344 // sections do not interfere with other formatting in the node's content.
345 $max_weight = -10000;
346 $last_section = '';
347 foreach($all_links as $section => $values) {
348 // Only add the section if there are links, and section type is "inline".
349 if (!empty($values['links']) && (empty($values['type']) || $values['type'] == 'inline')) {
350 $weight = !empty($values['weight']) ? $values['weight'] : 0;
351 $node->content[$section] = array(
352 '#value' => theme('item_list', $values['links'], isset($values['name']) ? $values['name'] : NULL),
353 '#weight' => !empty($values['weight']) ? $values['weight'] : 0,
354 '#prefix' => '<div class="project-links-section" id="project-links-section-'. $section .'">',
355 '#suffix' => '</div>',
356 );
357 if (!empty($values['clear'])) {
358 $node->content[$section]['#suffix'] .= '<br style="clear:both;" />';
359 }
360 if ($weight >= $max_weight) {
361 $last_section = $section;
362 $max_weight = $weight;
363 }
364 }
365 }
366 // We only want to add a clearing <br> after the final section if that
367 // section didn't already add a clear for itself (e.g. the heaviest
368 // section might already clear from hook_project_page_link_alter()).
369 if (empty($all_links[$last_section]['clear']) && !empty($last_section)) {
370 $node->content[$last_section]['#suffix'] .= '<br style="clear:both;" />';
371 }
372 }
373 return $node;
374 }
375
376 /**
377 * hook_nodeapi() implementation specific for project nodes.
378 * @see project_nodeapi().
379 */
380 function project_project_nodeapi(&$node, $op, $arg) {
381 $language = isset($node->language) ? $node->language : '';
382 switch ($op) {
383 case 'insert':
384 _project_save_taxonomy($node);
385 if (module_exists('path') && variable_get('project_enable_alias', TRUE)) {
386 path_set_alias("node/$node->nid", 'project/'. $node->project['uri'], NULL, $language);
387 }
388 break;
389
390 case 'update':
391 _project_save_taxonomy($node);
392 if (module_exists('path') && variable_get('project_enable_alias', TRUE)) {
393 path_set_alias("node/$node->nid"); // Clear existing alias.
394 path_set_alias("node/$node->nid", 'project/'. $node->project['uri'], NULL, $language);
395 }
396 break;
397 }
398 }
399
400 function project_project_insert($node) {
401 db_query("INSERT INTO {project_projects} (nid, uri, homepage, changelog, cvs, demo, screenshots, documentation, license) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $node->nid, $node->project['uri'], $node->project['homepage'], $node->project['changelog'], $node->project['cvs'], $node->project['demo'], $node->project['screenshots'], $node->project['documentation'], $node->project['license']);
402 // project_release_scan_directory($node->project['uri']);
403 }
404
405 function project_project_update($node) {
406 db_query("UPDATE {project_projects} SET uri = '%s', homepage = '%s', changelog = '%s', cvs = '%s', demo = '%s', screenshots = '%s', documentation = '%s', license = '%s' WHERE nid = %d", $node->project['uri'], $node->project['homepage'], $node->project['changelog'], $node->project['cvs'], $node->project['demo'], $node->project['screenshots'], $node->project['documentation'], $node->project['license'], $node->nid);
407 // project_release_scan_directory($node->project['uri']);
408 }
409
410 function project_project_delete($node) {
411 db_query('DELETE FROM {project_projects} WHERE nid = %d', $node->nid);
412 }
413
414 function project_project_retrieve($key = 0) {
415 if (!is_numeric($key)) {
416 $nid = project_get_nid_from_uri($key);
417 }
418 $node = node_load($nid);
419 return ($node->type == 'project_project') ? $node : NULL;
420 }
421
422 function project_cvs($nid = 0) {
423 if ($project = node_load($nid)) {
424 if (node_access('view', $project)) {
425 $_REQUEST['nid'] = $nid;
426 $output = module_invoke('cvs', 'show_messages');
427 drupal_set_title(t('CVS messages for %name', array('%name' => $project->title)));
428 project_project_set_breadcrumb($project, TRUE);
429 return $output;
430 }
431 else {
432 drupal_access_denied();
433 }
434 }
435 else {
436 drupal_not_found();
437 }
438 }
439
440 function _project_save_taxonomy(&$node) {
441 if (project_use_taxonomy() && $node->project_type) {
442 // First, clear out all terms from the project-specific taxonomy
443 // in this node. We'll re-add the right ones based on what's saved.
444 // This way, we're sure to clear out things that have been changed.
445 $vid = _project_get_vid();
446 $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
447 $args = array($node->nid, $node->vid);
448 $terms = array();
449 while ($item = db_fetch_object($result)) {
450 $terms[] = $item->tid;
451 }
452 if (count($terms) > 1) {
453 $sql = 'DELETE FROM {term_node} WHERE nid = %d AND vid = %d AND tid IN ('. db_placeholders($terms) .')';
454 $args = array_merge($args, $terms);
455 db_query($sql, $args);
456 }
457 $tid = $node->project_type;
458 _project_db_save_taxonomy($node->nid, $tid, $node->vid);
459 $tid_field = 'tid_' . $tid;
460 if (isset($node->$tid_field)) {
461 foreach ($node->$tid_field as $tid) {
462 _project_db_save_taxonomy($node->nid, $tid, $node->vid);
463 }
464 }
465 }
466 }
467
468 function _project_db_save_taxonomy($nid, $tid, $vid) {
469 db_query('INSERT INTO {term_node} (nid, tid, vid) VALUES (%d, %d, %d)', $nid, $tid, $vid);
470 }
471
472 /**
473 * Adds the 'project-taxonomy-element' div to the project_type
474 * and term select box on the project_project node form.
475 */
476 function theme_project_project_node_form_taxonomy($form) {
477 $output = '';
478 foreach (element_children($form) as $child) {
479 $output .= '<div class="project-taxonomy-element">';
480 $output .= drupal_render($form[$child]);
481 $output .= '</div>';
482 }
483 return $output;
484 }
485
486 /**
487 * Build a nested array of sections of links to display on project_project node pages.
488 */
489 function project_get_project_link_info($node = NULL) {
490 static $all_links;
491
492 // We only need to build the links array once per page.
493 if (is_array($all_links)) {
494 return $all_links;
495 }
496
497 // Resources section
498 $all_links['resources'] = array(
499 'name' => t('Resources'),
500 'weight' => 4,
501 'type' => 'inline',
502 );
503 foreach (array('homepage' => t('Home page'), 'documentation' => t('Read documentation'), 'license' => t('Read license'), 'changelog' => t('Read complete log of changes'), 'demo' => t('Try out a demonstration'), 'screenshots' => t('Look at screenshots')) as $uri => $name) {
504 if (!empty($node->project[$uri])) {
505 $all_links['resources']['links'][$uri] = l($name, $node->project[$uri]);
506 }
507 }
508
509 // Developer section
510 $all_links['development'] = array(
511 'name' => t('Development'),
512 'weight' => 8,
513 'type' => 'inline',
514 );
515 $links = array();
516 if (!empty($node->project['cvs'])) {
517 $links['browse_repository'] = l(t('Browse the CVS repository'), $node->project['cvs']);
518 }
519
520 if (project_use_cvs($node)) {
521 $links['view_cvs_messages'] = l(t('View CVS messages'), 'project/cvs/'. $node->nid);
522 }
523 $all_links['development']['links'] = $links;
524
525 // Allow other modules to add sections of links and/or links to the sections defined above.
526 drupal_alter('project_page_link', $all_links, $node);
527
528 return $all_links;
529 }
530

  ViewVC Help
Powered by ViewVC 1.1.2