/[drupal]/contributions/modules/drupalorg/drupalorg_project/drupalorg_project.module
ViewVC logotype

Contents of /contributions/modules/drupalorg/drupalorg_project/drupalorg_project.module

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


Revision 1.24 - (show annotations) (download) (as text)
Tue Aug 18 17:48:20 2009 UTC (3 months, 1 week ago) by drumm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.23: +5 -5 lines
File MIME type: text/x-php
#512824 fix by greggles.
1 <?php
2 // $Id: drupalorg_project.module,v 1.23 2009/08/01 05:19:20 drumm Exp $
3
4 /**
5 * @file
6 * Project* module related customizations used on drupal.org.
7 */
8
9 /**
10 * Release node ids for issues we're currently tracking (Drupal 7).
11 */
12 define('DRUPALORG_ISSUE_RIDS', '156281');
13
14 /**
15 * The CVS user role. Added to users with a CVS account.
16 */
17 define('DRUPALORG_CVS_USER_ROLE', 8);
18
19 /**
20 * The required simplenews newsletter tid for CVS account holders.
21 */
22 define('DRUPALORG_CVS_NEWSLETTER', 118);
23
24 /**
25 * URL for the Drupal Security Team.
26 */
27 define('DRUPALORG_SECURITY_TEAM_URL', 'http://drupal.org/security-team');
28
29 /**
30 * URL for links to contact the security team
31 */
32 define('DRUPALORG_SECURITY_CONTACT_URL', 'http://drupal.org/security-team#report-issue');
33
34 /**
35 * URL for the handbook page that describes the "Release type" vocabulary.
36 */
37 define('DRUPALORG_RELEASE_TYPE_HANDBOOK_URL', 'http://drupal.org/handbook/cvs/releases/types#type');
38
39 /**
40 * URL specifically to describe the "Security update" term.
41 * For now, the general page for release types is the best we've got.
42 */
43 define('DRUPALORG_SECURITY_UPDATE_HANDBOOK_URL', DRUPALORG_RELEASE_TYPE_HANDBOOK_URL);
44
45 // == Basic core hooks =========================================================
46
47 /**
48 * Implementation of hook_menu().
49 */
50 function drupalorg_project_menu() {
51 // Menu items for patch bingo.
52 $items['bug-bingo'] = array(
53 'title' => 'Bug bingo',
54 'page callback' => 'drupalorg_project_bingo_jump',
55 'page arguments' => array('bug-core'),
56 'access arguments' => array('access content'),
57 'type' => MENU_SUGGESTED_ITEM,
58 );
59 $items['contrib-bug-bingo'] = array(
60 'title' => 'Contrib bug bingo',
61 'page callback' => 'drupalorg_project_bingo_jump',
62 'page arguments' => array('bug-contrib'),
63 'access arguments' => array('access content'),
64 'type' => MENU_SUGGESTED_ITEM,
65 );
66 $items['patch-bingo'] = array(
67 'title' => 'Patch bingo',
68 'page callback' => 'drupalorg_project_bingo_jump',
69 'page arguments' => array('patch-core'),
70 'access arguments' => array('access content'),
71 'type' => MENU_SUGGESTED_ITEM,
72 );
73 $items['contrib-patch-bingo'] = array(
74 'title' => 'Contrib patch bingo',
75 'page callback' => 'drupalorg_project_bingo_jump',
76 'page arguments' => array('patch-contrib'),
77 'access arguments' => array('access content'),
78 'type' => MENU_SUGGESTED_ITEM,
79 );
80
81 // Some redirect menu items.
82 $items['project/drupal project'] = array(
83 'access arguments' => array('access content'),
84 'page callback' => 'drupal_goto',
85 'page arguments' => array('project/drupal'),
86 'type' => MENU_CALLBACK,
87 );
88 $items['download'] = array(
89 'access arguments' => array('access content'),
90 'page callback' => 'drupal_goto',
91 'page arguments' => array('project/drupal'),
92 'type' => MENU_CALLBACK,
93 );
94 return $items;
95 }
96
97 /**
98 * Implementation of hook_help().
99 */
100 function drupalorg_project_help($path, $arg) {
101 switch ($path) {
102 case 'project/usage':
103 case 'project/usage/%':
104 return '<p>'. t('These statistics are incomplete; only Drupal websites using the <a href="!update_status_url">Update Status</a> module are included in the data. As this module is now included with the download of Drupal since version 6.x, the data is heavily biased toward newer sites. <a href="!usage_description_url">Read more information about how these statistics are calculated.</a>', array('!update_status_url' => url('project/update_status'), '!usage_description_url' => url('node/329620'))) .'</p>';
105 }
106
107 if ($arg[0] == 'project' && strtolower($arg[1]) == 'modules') {
108 return t('You can also view an <a href="@list_url">alphabetical list of projects</a> that includes all projects but only their names.', array('@list_url' => url('node/206666')));
109 }
110 if ($arg[0] == 'project' && $arg[1] == 'issues-term' && $arg[2] == '346') {
111 return '<p>'. t('A more detailed overview of the issues can be seen in the Community Initiatives handbook section titled <a href="http://drupal.org/node/362117">Upgrade Drupal.org from Drupal 5 to 6</a>.') .'</p>';
112 }
113 }
114
115 /**
116 * Implementation of hook_cron().
117 */
118 function drupalorg_project_cron() {
119 drupalorg_project_issue_counts();
120 }
121
122 // == Issue counter ============================================================
123
124 /**
125 * Pulls issue counts for various issue queues on drupal.org.
126 *
127 * Summarize issue counts for different types of issues for DRUPALORG_ISSUE_RIDS releases.
128 */
129 function drupalorg_project_issue_counts() {
130 $issue_counts['Pending bugs'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND category = 'bug' AND sid = 1 AND priority IN (1,2) AND rid IN (". DRUPALORG_ISSUE_RIDS .")"));
131 $issue_counts['Critical issues'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND category IN ('bug', 'task') AND sid IN (1,8,13,14) AND priority = 1 AND rid IN (". DRUPALORG_ISSUE_RIDS .")"));
132 $issue_counts['Patch queue'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND sid IN (8,13,14) AND rid IN (". DRUPALORG_ISSUE_RIDS .")"));
133 $issue_counts['Patches to review'] = db_result(db_query("SELECT COUNT(*) FROM {project_issues} pi INNER JOIN {node} n ON pi.nid = n.nid WHERE n.status = 1 AND pid = 3060 AND sid IN (8) AND rid IN (". DRUPALORG_ISSUE_RIDS .")"));
134 variable_set('drupalorg_project_issue_counts', $issue_counts);
135 }
136
137 // == Altering of various forms ================================================
138
139 /**
140 * Implementation of hook_form_alter().
141 */
142 function drupalorg_project_form_alter(&$form, $form_state, $form_id) {
143 // Add a description for the Priority and Status values.
144 if ($form_id == 'project_issue_node_form' || ($form_id == 'comment_form' && !empty($form['original_issue']['issue_info']))) {
145 $priority_status_description = array(
146 // The "standard" class adds a clear so the description is positioned
147 // below the floated pull-downs. "fieldset-description" is from
148 // bluebeach to make the font size smaller.
149 '#prefix' => '<div class="standard fieldset-description">',
150 '#value' => t('Descriptions of the <a href="!priority_url">Priority</a> and <a href="!status_url">Status</a> values can be found in the <a href="!issue_queue_url">Issue queue handbook</a>.', array('!priority_url' => '/node/45111', '!status_url' => '/node/156119', '!issue_queue_url' => '/node/317')),
151 '#suffix' => '</div>',
152 );
153
154 // Make the "Issue tags" vocabulary not so prominent on issue nodes.
155 $form['taxonomy']['#type'] = 'fieldset';
156 $form['taxonomy']['#title'] = t('Tags');
157 $form['taxonomy']['#collapsible'] = TRUE;
158 $form['taxonomy']['#collapsed'] = TRUE;
159 // We want this fieldset at the very bottom, just above the buttons, but
160 // we need different weights for that depending on if it's a new issue or
161 // a follow-up comment.
162 if ($form_id == 'project_issue_node_form') {
163 $form['taxonomy']['#weight'] = 35;
164 $form['issue_info']['description'] = $priority_status_description;
165 }
166 else {
167 $form['taxonomy']['#weight'] = 4;
168 $form['original_issue']['issue_info']['description'] = $priority_status_description;
169 }
170
171 // Prefill values from $_GET.
172 if (isset($_GET['version'])) {
173 $form['project_info']['rid']['#default_value'] = $_GET['version'];
174 }
175 if (isset($_GET['component'])) {
176 $form['project_info']['component']['#default_value'] = $_GET['component'];
177 }
178 if (isset($_GET['categories'])) {
179 $form['issue_info']['category']['#default_value'] = $_GET['categories'];
180 }
181 if (isset($_GET['priorities'])) {
182 $form['issue_info']['priority']['#default_value'] = $_GET['priorities'];
183 }
184 if (isset($_GET['assigned'])) {
185 $form['issue_info']['assigned']['#default_value'] = $_GET['assigned'];
186 }
187 if (isset($_GET['status'])) {
188 $form['issue_info']['sid']['#default_value'] = $_GET['status'];
189 }
190 if (isset($_GET['title'])) {
191 $form['issue_details']['title']['#default_value'] = $_GET['title'];
192 }
193 if (isset($_GET['body'])) {
194 $form['issue_details']['body']['#default_value'] = $_GET['body'];
195 }
196 if (isset($_GET['tags'])) {
197 $form['taxonomy']['#collapsed'] = FALSE;
198 $form['taxonomy']['tags'][reset(array_keys($form['taxonomy']['tags']))]['#default_value'] = $_GET['tags'];
199 }
200 }
201
202 // Add security update related checks to release forms.
203 if ($form_id == 'project_release_node_form') {
204 drupalorg_project_release_node_form_alter($form, $form_state);
205 }
206
207 // Add CVS maintainer newsletter signup to users when getting a CVS account.
208 if ($form_id == 'cvs_user_edit_form') {
209 $form['#submit'][] = 'drupalorg_project_cvs_user_edit_submit';
210 }
211
212 // Deprecate the "license" field, as it must always be GPLv2+ anyway.
213 if ($form_id == 'project_project_node_form') {
214 if (! $form['project']['license']['#default_value']) {
215 $form['project']['license'] = array(
216 '#type' => 'item',
217 '#title' => $form['project']['license']['#title'],
218 '#value' => t('<a href="@link_url">@link_name</a>', array(
219 // Constants defined in CVS module.
220 '@link_url' => CVS_LICENSE_LINK,
221 '@link_name' => CVS_LICENSE,
222 )),
223 );
224 }
225 }
226 }
227
228 // == Security release restrictions ============================================
229
230 /**
231 * Alter release node forms properly for security updates.
232 *
233 * Ensure that only privileged users can modify a security release tag and when
234 * people add this tag, they get to know about the process.
235 */
236 function drupalorg_project_release_node_form_alter(&$form, $form_state) {
237 if (!empty($form['taxonomy'])) {
238 $vid = drupalorg_project_get_release_type_vid();
239 $security_tid = drupalorg_project_get_security_update_tid();
240 if (!empty($form['taxonomy'][$vid])) {
241 $form['taxonomy'][$vid]['#weight'] = 10;
242 if (empty($form['nid']['#value'])) {
243 // Adding a new release.
244 if (!empty($form['project_release']['rebuild']['#value'])) {
245 // This is a -dev, don't let anyone mark it a security update.
246 _drupalorg_project_remove_security_update($form);
247 }
248 else {
249 // Regular release
250 $form['taxonomy'][$vid]['#description'] = t('<a href="@handbook_url">What is a release type?</a>', array('@handbook_url' => DRUPALORG_RELEASE_TYPE_HANDBOOK_URL));
251 // Add wrapper div for drupalorg_project.js
252 $form['taxonomy'][$vid]['#prefix'] = '<div class="release-type-select">';
253 $form['taxonomy'][$vid]['#suffix'] = '</div>';
254 $form['#validate'][] = 'drupalorg_project_security_release_form_validate';
255 $confirm_class = 'security-update-confirm';
256 // Hide the confirmation checkbox on page load unless 'Security
257 // update' is already selected.
258 if (empty($form_state['values']['taxonomy'][$vid]) || (array_search($security_tid, $form_state['values']['taxonomy'][$vid]) === FALSE)) {
259 $confirm_class .= ' js-hide';
260 }
261 $form['security_update_confirm'] = array(
262 '#type' => 'checkbox',
263 '#title' => t('Are you sure you want to mark this release as a <a href="@security_update_url">Security update</a>?', array('@security_update_url' => DRUPALORG_SECURITY_UPDATE_HANDBOOK_URL)),
264 '#prefix' => '<div class="'. $confirm_class .'">',
265 '#suffix' => '</div>',
266 '#weight' => -2,
267 '#description' => t('If you select %security_update, your release will not be published without the manual intervention of the <a href="@security_url">Drupal Security Team</a>. You should have already <a href="@contact_url">contacted the Security Team</a> to coordinate a security advisory (SA) for your release before you committed any security-related patches.', array('%security_update' => t('Security update'), '@security_url' => DRUPALORG_SECURITY_TEAM_URL, '@contact_url' => DRUPALORG_SECURITY_CONTACT_URL)),
268 '#default_value' => !empty($form_state['values']['security_update_confirm']),
269 );
270 $drupalorg_project_path = drupal_get_path('module', 'drupalorg_project');
271 drupal_add_js($drupalorg_project_path .'/drupalorg_project.js');
272 drupal_add_css($drupalorg_project_path .'/drupalorg_project.css');
273 }
274 }
275 else {
276 // Editing an existing release.
277 if (array_search($security_tid, $form['taxonomy'][$vid]['#default_value']) !== FALSE) {
278 // If this release is already marked as a Security update, don't
279 // let regular users change it any futher.
280 if (!user_access('administer projects')) {
281 $form['taxonomy'][$vid]['#disabled'] = TRUE;
282 $form['taxonomy'][$vid]['#value'] = $form['taxonomy'][$vid]['#default_value'];
283 }
284 $form['taxonomy'][$vid]['#description'] = t('<a href="@handbook_url">What is a release type?</a> Since this release is already marked as a %security_update, you can no longer change the release type. If you believe you need to do so for some reason, you should <a href="@contact_url">contact</a> the <a href="@security_url">Drupal Security Team</a>.', array('@handbook_url' => DRUPALORG_RELEASE_TYPE_HANDBOOK_URL, '%security_update' => t('Security update'), '@security_url' => DRUPALORG_SECURITY_TEAM_URL, '@contact_url' => DRUPALORG_SECURITY_CONTACT_URL));
285 }
286 else {
287 // Not a Security update, remove that option entirely if this is
288 // either a -dev snapshot or a non-admin user.
289 if (!empty($form['#node']->rebuild) || !user_access('administer projects')) {
290 _drupalorg_project_remove_security_update($form);
291 }
292 $form['taxonomy'][$vid]['#description'] = t('<a href="@handbook_url">What is a release type?</a>', array('@handbook_url' => DRUPALORG_RELEASE_TYPE_HANDBOOK_URL));
293 }
294 }
295 }
296 }
297 }
298
299 /**
300 * Utility function to remove the security update term on node forms.
301 */
302 function _drupalorg_project_remove_security_update(&$form) {
303 $vid = drupalorg_project_get_release_type_vid();
304 $security_tid = drupalorg_project_get_security_update_tid();
305 foreach ($form['taxonomy'][$vid]['#options'] as $i => $option) {
306 if (!empty($option->option)) {
307 $tid = key($option->option);
308 if ($tid == $security_tid) {
309 unset($form['taxonomy'][$vid]['#options'][$i]);
310 return;
311 }
312 }
313 }
314 }
315
316 /**
317 * Set the project_release_type_vid Drupal variable and return its value.
318 */
319 function drupalorg_project_get_release_type_vid() {
320 static $vid = 0;
321 if (empty($vid)) {
322 $vid = variable_get('project_release_type_vid', 0);
323 if (empty($vid)) {
324 $vid = db_result(db_query("SELECT v.vid FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = 'project_release' AND v.name = 'Release type'"));
325 variable_set('project_release_type_vid', $vid);
326 }
327 }
328 return $vid;
329 }
330
331 /**
332 * Set the project_release_type_security_update_tid Drupal variable and return its value.
333 */
334 function drupalorg_project_get_security_update_tid() {
335 static $tid = 0;
336 if (empty($tid)) {
337 $tid = variable_get('project_release_type_security_update_tid', 0);
338 if (empty($tid)) {
339 $tid = db_result(db_query("SELECT tid FROM {term_data} WHERE vid = %d AND name = '%s'", drupalorg_project_get_release_type_vid(), 'Security update'));
340 variable_set('project_release_type_security_update_tid', $tid);
341 }
342 }
343 return $tid;
344 }
345
346 /**
347 * Form validation function for security release tag check.
348 */
349 function drupalorg_project_security_release_form_validate($form, &$form_state) {
350 $vid = drupalorg_project_get_release_type_vid();
351 $security_tid = drupalorg_project_get_security_update_tid();
352 if (!empty($form_state['values']['taxonomy'][$vid][$security_tid]) && empty($form_state['values']['security_update_confirm'])) {
353 // Ensure the user confirms that this release should be marked security.
354 form_set_error('security_update_confirm', t('You must confirm you want this release to be a <a href="@security_update_url">Security update</a>', array('@security_update_url' => DRUPALORG_SECURITY_UPDATE_HANDBOOK_URL)));
355 }
356 }
357
358 // == CVS maintainer housekeeping ==============================================
359
360 /**
361 * Add the user to the CVS maintainer role, when she gets an account.
362 */
363 function drupalorg_project_cvs_user_edit_submit($form, &$form_state) {
364 global $language;
365
366 if (isset($form_state['values']['cvs_status'])) {
367 $account = user_load(array('uid' => $form_state['values']['cvs_uid']));
368 switch ($form_state['values']['cvs_status']) {
369 case CVS_APPROVED:
370 db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $form_state['values']['cvs_uid'], DRUPALORG_CVS_USER_ROLE);
371 break;
372 case CVS_DISABLED:
373 db_query('DELETE FROM {users_roles} WHERE uid = %d AND rid = %d', $form_state['values']['cvs_uid'], DRUPALORG_CVS_USER_ROLE);
374 break;
375 }
376 // Clear cache for this user, so that the user will see new functionality.
377 cache_clear_all($form_state['values']['cvs_uid'] .':'. $language->language, 'cache_menu');
378 }
379 }
380
381 // == Project links ============================================================
382
383 /**
384 * Implemenation of hook_project_page_link_alter().
385 */
386 function drupalorg_project_project_page_link_alter(&$links, $node) {
387 // Link to security handbook page.
388 $links['development']['links']['report_security_issue'] = l(t('Report a security issue'), 'security-team');
389 }
390
391 // == Utility functions ========================================================
392
393 /**
394 * Project issue URL generator for Drupal issues.
395 *
396 * @param $query
397 * Array of array of options to pass on in the URL:
398 * - version (array of release node ids)
399 * - status (array of status ids)
400 * - priorities (array of prioristy ids)
401 * - categories (array of category names)
402 * @param $absolute
403 * Set to TRUE to get an absolute URL with http://drupal.org/...
404 */
405 function drupalorg_project_issue_url($query = array(), $absolute = FALSE) {
406 return url('project/issues/search/drupal', array('query' => $query, 'absolute' => $absolute));
407 }
408
409 // == Node overrides ===========================================================
410
411 /**
412 * Implementation of hook_nodeapi().
413 */
414 function drupalorg_project_nodeapi(&$node, $op = 'view', $teaser = FALSE, $page = FALSE) {
415 if ($op == 'view' && $page) {
416 $extra = '';
417 switch ($node->nid) {
418 case 206666: // List projects by Drupal core version.
419 $extra = drupalorg_project_list_by_core();
420 break;
421 case 199251: // List projects with CVS RSS feeds.
422 $extra = drupalorg_project_list_cvs_rss();
423 break;
424 case 97084: // List branches ever created in contrib.
425 $extra = drupalorg_project_list_branches_contrib();
426 break;
427 case 93997: // List branches ever created in core.
428 $extra = drupalorg_project_list_branches_core();
429 break;
430 case 9730:
431 $url_queue = drupalorg_project_issue_url(
432 array(
433 'status' => array(8, 13, 14),
434 ),
435 TRUE /* Absolute */
436 );
437 header('Location: '. $url_queue);
438 exit;
439
440 case 9731:
441 $url_bugs = drupalorg_project_issue_url(
442 array(
443 'status' => array(1),
444 'categories' => array('bug'),
445 ),
446 TRUE /* Absolute */
447 );
448 header('Location: '. $url_bugs);
449 exit;
450
451 case 9732:
452 $url_tasks = drupalorg_project_issue_url(
453 array(
454 'status' => array(1),
455 'categories' => array('task'),
456 ),
457 TRUE /* Absolute */
458 );
459 header('Location: '. $url_tasks);
460 exit;
461
462 case 133282:
463 drupal_goto('patch/create');
464 exit;
465 }
466 $node->content['body']['#value'] .= $extra;
467 }
468 }
469
470 /**
471 * List projects by Drupal core version.
472 */
473 function drupalorg_project_list_by_core() {
474 $output = '';
475
476 $tags = array(
477 'DRUPAL-4-7' => t('Drupal 4.7.x'),
478 'DRUPAL-5' => t('Drupal 5.x'),
479 'DRUPAL-6' => t('Drupal 6.x'),
480 );
481
482 foreach ($tags as $tag => $label) {
483 $top_list[] = l($tag, $_GET['q'], array('fragment' => $tag));
484 $projects = db_query("SELECT DISTINCT p.nid, n.title, p.uri
485 FROM {project_projects} p INNER JOIN {project_release_nodes} r ON p.nid = r.pid INNER JOIN {node} n ON n.nid = p.nid INNER JOIN {term_node} tn ON p.nid = tn.nid
486 WHERE tn.tid = 14 AND r.tag LIKE '%s%%'
487 ORDER BY n.title", $tag);
488 $list = array();
489 while ($project = db_fetch_object($projects)) {
490 $list[] = l($project->title, "project/$project->uri", array('html' => TRUE));
491 }
492 if (!empty($list)) {
493 $output .= theme('item_list', $list, "<a name=\"$tag\">$label</a>");
494 }
495 }
496 if (!empty($top_list)) {
497 $output = theme('item_list', $top_list) . $output;
498 }
499
500 return $output;
501 }
502
503 /**
504 * List projects with CVS RSS links.
505 */
506 function drupalorg_project_list_cvs_rss() {
507 $count_query = "SELECT COUNT(*) FROM {node} WHERE type = 'project_project' AND status = 1";
508 $total = db_result(db_query($count_query));
509 $header = array(array('data' => t('Number'), 'field' => 'nid', 'sort' => 'desc'), t('Title'), t('CVS/RSS'));
510 $result = pager_query("SELECT nid, title, status FROM {node} WHERE type = 'project_project' AND status = 1 ". tablesort_sql($header), 100, 0, $count_query);
511 $rows = array();
512 $page = (int)$_GET['page'];
513 $count = $total - $page * 100;
514 while($row = db_fetch_array($result)) {
515 $url = url('cvs', array('query' => 'rss=true&nid='. $row['nid'], 'absolute' => TRUE));
516 $rows[]= array($count--, l($row['title'], 'node/'. $row['nid']), theme('feed_icon', $url, $row['title']));
517 }
518 drupal_set_message("There are $total projects.");
519
520 return theme('table', $header, $rows). theme('pager');
521 }
522
523 /**
524 * Show branches ever created in contrib based on CVS module data.
525 */
526 function drupalorg_project_list_branches_contrib() {
527 $output = '<ul>';
528 $query= db_query("SELECT DISTINCT tag, COUNT(*) AS total
529 FROM {cvs_tags}
530 WHERE nid != 3060 AND nid != 0 AND tag RLIKE 'DRUPAL' AND branch = 1
531 GROUP BY tag ORDER BY tag DESC");
532 while($tag = db_fetch_object($query)) {
533 $output .= ' <li>' . check_plain($tag->tag) . ' ('. format_plural($tag->total, '1 project', '@count projects') .')</li>';
534 }
535 $output .= '</ul>';
536 return $output;
537 }
538
539 /**
540 * Show branches ever created in core based on CVS module data.
541 */
542 function drupalorg_project_list_branches_core() {
543 $output = '<ul>';
544 $query= db_query("SELECT tag
545 FROM {cvs_tags}
546 WHERE nid = 3060 AND branch = 1 AND tag != 'DRUPAL-3-00'
547 ORDER BY tag DESC");
548 while($tag = db_fetch_object($query)) {
549 $output .= ' <li>' . check_plain($tag->tag) . '</li>';
550 }
551 $output .= '</ul>';
552 $output .= '<a href="#tags"><h2 id="tags">Available tags</h2></a>';
553 $output .= '<p>The tags currently available in Drupal core are:</p>';
554 $output .= '<ul>';
555
556 $query= db_query("SELECT tag
557 FROM {cvs_tags}
558 WHERE nid = 3060 AND branch = 0 AND tag RLIKE 'DRUPAL-'
559 ORDER BY tag DESC");
560 while($tag = db_fetch_object($query)) {
561 $output .= ' <li>' . check_plain($tag->tag) . '</li>';
562 }
563 $output .= '</ul>';
564 return $output;
565 }
566
567 // == Bug bingo ================================================================
568
569 /**
570 * SQL randomizer for issues.
571 */
572 function drupalorg_project_bingo_jump($type = NULL) {
573 $sql = array(
574 // Bug, core.
575 'bug-core' => "SELECT nid FROM {project_issues} WHERE sid IN (1) AND category = 'bug' AND pid = 3060 ORDER BY RAND() LIMIT 1",
576 // Bug, contrib.
577 'bug-contrib' => "SELECT nid FROM {project_issues} WHERE sid IN (1) AND category = 'bug' AND pid != 3060 ORDER BY RAND() LIMIT 1",
578 // Patch, core.
579 'patch-core' => "SELECT nid FROM {project_issues} WHERE sid IN (8,13,14) AND pid = 3060 ORDER BY RAND() LIMIT 1",
580 // Patch, contrib.
581 'patch-contrib' => "SELECT nid FROM {project_issues} WHERE sid IN (8,13,14) AND pid != 3060 ORDER BY RAND() LIMIT 1",
582 );
583 if (!isset($type) || !isset($sql[$type])) {
584 $type = 'bug-core';
585 }
586
587 $nid = db_result(db_query($sql[$type]));
588 if ($_GET['stop'] != 1) {
589 drupal_goto('node/'. $nid);
590 }
591 }
592
593 /**
594 * Implementation of hook_block().
595 */
596 function drupalorg_project_block($op = 'list', $delta = 0, $edit = array()) {
597 switch ($op) {
598 case 'list':
599 $blocks[0]['info'] = t('Contributor links');
600 return $blocks;
601
602 case 'view':
603 $block['subject'] = t('Contributor links');
604 $block['content'] = drupalorg_project_bingo_block_output();
605 return $block;
606 }
607 }
608
609 /**
610 * Output links with issue counts for different types of issues.
611 */
612 function drupalorg_project_bingo_block_output() {
613 $counts = variable_get('drupalorg_project_issue_counts', array());
614
615 $counts_pending = $counts['Pending bugs'];
616 $counts_critical = $counts['Critical issues'];
617 $counts_queue = $counts['Patch queue'];
618 $counts_review = $counts['Patches to review'];
619
620 $versions = array_map('trim', explode(',', DRUPALORG_ISSUE_RIDS));
621 $url_pending = drupalorg_project_issue_url(
622 array(
623 'version' => $versions,
624 'status' => array(1),
625 'priorities' => array(1, 2),
626 'categories' => array('bug'),
627 )
628 );
629 $url_critical = drupalorg_project_issue_url(
630 array(
631 'version' => $versions,
632 'status' => array(1, 8, 13, 14),
633 'priorities' => array(1),
634 'categories' => array('bug', 'task'),
635 )
636 );
637 $url_queue = drupalorg_project_issue_url(
638 array(
639 'version' => $versions,
640 'status' => array(8, 13, 14),
641 )
642 );
643 $url_review = drupalorg_project_issue_url(
644 array(
645 'version' => $versions,
646 'status' => array(8, 14),
647 )
648 );
649
650 $output = <<<EOT
651 <div class="item-list">
652 <ul>
653 <li><a href="/community-initiatives">Community initiatives</a></li>
654 <li>
655 <strong>Queues</strong>
656 <ul>
657 <li>
658 <a href="/project/issues/user">
659 My issues
660 </a>
661 </li>
662 <li>
663 <a href="$url_pending">
664 $counts_pending Pending bugs (D7)
665 </a>
666 </li>
667 <li>
668 <a href="$url_critical">
669 $counts_critical Critical issues (D7)
670 </a>
671 </li>
672 <li>
673 <a href="$url_queue">
674 $counts_queue Patch queue (D7)
675 </a>
676 </li>
677 <li>
678 <a href="$url_review">
679 $counts_review Patches to review (D7)
680 </a>
681 </li>
682 </ul>
683 </li>
684 <li><strong>Play patch bingo!</strong>
685 <ul>
686 <li><a href="/patch-bingo" title="Select a random patch for review">Drupal core</a></li>
687 <li><a href="/contrib-patch-bingo" title="Select a random patch for review from the contributions">Contributions</a></li>
688 </ul>
689 </li>
690 <li><strong>Play bug bingo!</strong>
691 <ul>
692 <li><a href="/bug-bingo" title="Select a random bug to fix">Drupal core</a></li>
693 <li><a href="/contrib-bug-bingo" title="Select a random bug to fix from the contributions">Contributions</a></li>
694 </ul></li>
695 <li><a href="/mailing-lists">Mailing list archives</a></li>
696 <li><a href="/project/issues/webmasters">Drupal.org webmasters</a></li>
697 <li><a href="/project/issues/infrastructure">Drupal.org server administrators</a></li>
698 <li><strong>Web links</strong>
699 <ul>
700 <li><a href="/planet">Planet Drupal</a></li>
701 <li><a href="/talk">Drupal talk</a></li>
702 <li><a href="http://groups.drupal.org/drupal-dojo">Drupal dojo</a></li>
703 </ul></li>
704 </ul>
705 </div>
706 EOT;
707 return $output;
708 }
709
710 /**
711 * Implementation of template_preprocess_drupalorg_home().
712 *
713 * @todo
714 * Add caching.
715 */
716 function drupalorg_project_preprocess_drupalorg_home(&$vars) {
717 $result = db_query_range("SELECT m.*, u.name, u.uid FROM {cvs_messages} m INNER JOIN {users} u ON m.uid = u.uid ORDER BY m.created DESC", 0, 5);
718 $recent_updates = '';
719 while ($message = db_fetch_object($result)) {
720 $recent_updates .= '<h6>'. l(truncate_utf8($message->message, 80) .'...', 'cvs', array('query' => 'commit='. $message->cid)) .'</h6><p class="submitted">'. theme('node_submitted', $message) .'</p>';
721 }
722 // We have no place to link this to in a nice way.
723 $recent_updates .= '<p>'. l(t('More commit messages...'), 'cvs') .'</p>';
724 $vars['tab_content_cvs'] = $recent_updates;
725 }

  ViewVC Help
Powered by ViewVC 1.1.2