| 1 |
<?php
|
| 2 |
// $Id: drupalorg_handbook.module,v 1.9 2009/04/11 20:54:13 courtney Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* Customizations and blocks for the handbook pages.
|
| 7 |
*/
|
| 8 |
|
| 9 |
/**
|
| 10 |
* Taxonomy vid for the "Drupal versions" vocabulary.
|
| 11 |
*/
|
| 12 |
define('DRUPALORG_HANDBOOK_VERSION_VID', 5);
|
| 13 |
|
| 14 |
// == Core hooks ===============================================================
|
| 15 |
|
| 16 |
/**
|
| 17 |
* Implementation of hook_nodeapi().
|
| 18 |
*/
|
| 19 |
function drupalorg_handbook_nodeapi(&$node, $op = 'view', $teaser = FALSE, $page = FALSE) {
|
| 20 |
if ($op == 'view' && $page) {
|
| 21 |
$extra = '';
|
| 22 |
switch ($node->nid) {
|
| 23 |
case 43633: // Handbook pages with comments
|
| 24 |
$extra = drupalorg_handbook_with_comments();
|
| 25 |
break;
|
| 26 |
case 43639: // Most popular handbooks
|
| 27 |
$extra = drupalorg_handbook_most_popular();
|
| 28 |
break;
|
| 29 |
case 322: // Mailing list subscription
|
| 30 |
$extra = drupal_get_form('lists_subscribe_form');
|
| 31 |
break;
|
| 32 |
case 13355: // Site maintainers
|
| 33 |
$extra = drupalorg_handbook_site_maintainers();
|
| 34 |
break;
|
| 35 |
case 109372: // Handbook maintainers
|
| 36 |
$extra = drupalorg_handbook_maintainers();
|
| 37 |
break;
|
| 38 |
case 14205: // Book contributors
|
| 39 |
$extra = drupalorg_handbook_book_contributors();
|
| 40 |
break;
|
| 41 |
case 263594: // Revision maintainers
|
| 42 |
$extra = drupalorg_handbook_revision_maintainers();
|
| 43 |
break;
|
| 44 |
case 23192: // Recent updates
|
| 45 |
$extra = drupalorg_handbook_display_recent_updates();
|
| 46 |
break;
|
| 47 |
}
|
| 48 |
$node->content['body']['#value'] .= $extra;
|
| 49 |
|
| 50 |
if (!empty($node->book['bid'])) {
|
| 51 |
// Prepend the handbook meta data to book bodies.
|
| 52 |
$node->content['body']['#value'] = drupalorg_handbook_meta_data($node) . $node->content['body']['#value'];
|
| 53 |
}
|
| 54 |
}
|
| 55 |
}
|
| 56 |
|
| 57 |
/**
|
| 58 |
* Implementation of hook_form_alter().
|
| 59 |
*/
|
| 60 |
function drupalorg_handbook_form_alter(&$form, $form_state, $form_id) {
|
| 61 |
// Force a revision log entry when editing existing book nodes.
|
| 62 |
if ($form_id == 'book_node_form' && isset($form['revision_information']['log']) && arg(1) != 'add') {
|
| 63 |
$form['revision_information']['log']['#required'] = TRUE;
|
| 64 |
$form['revision_information']['#collapsed'] = FALSE;
|
| 65 |
|
| 66 |
// Remove the IA vocabs from handbooks other than the Theming Guide.
|
| 67 |
if ($form['#node']->book['bid'] != 338057) {
|
| 68 |
$form['taxonomy'][40]['#access'] = FALSE;
|
| 69 |
$form['taxonomy']['tags'][42]['#access'] = FALSE;
|
| 70 |
}
|
| 71 |
else {
|
| 72 |
// Add a note for the IA stuff.
|
| 73 |
$form['taxonomy']['#description'] = t('The Theming guide has some additional vocabularies not found on other handbook pages,information type and subject matter, which are being used to assist our IA project. For more information on how to properly use these two vocabularies, please refer to the !guidelines.', array('!guidelines' => l('guidelines', 'node/548322')));
|
| 74 |
}
|
| 75 |
}
|
| 76 |
}
|
| 77 |
|
| 78 |
/**
|
| 79 |
* Gather information for the top of handbook pages.
|
| 80 |
*
|
| 81 |
* @param $node
|
| 82 |
* The node object.
|
| 83 |
* @return
|
| 84 |
* The HTML code for the book meta information.
|
| 85 |
*/
|
| 86 |
function drupalorg_handbook_meta_data($node) {
|
| 87 |
if (count($node->taxonomy)) {
|
| 88 |
// Group the terms by vid.
|
| 89 |
$taxonomy = array();
|
| 90 |
foreach ($node->taxonomy as $term_id => $term) {
|
| 91 |
$taxonomy[$term->vid][] = $term;
|
| 92 |
}
|
| 93 |
|
| 94 |
if (count($taxonomy[DRUPALORG_HANDBOOK_VERSION_VID])) {
|
| 95 |
// Make list of versions associated to handbook page.
|
| 96 |
$links = array();
|
| 97 |
foreach ($taxonomy[DRUPALORG_HANDBOOK_VERSION_VID] as $tag) {
|
| 98 |
$links[] = theme('drupalorg_handbook_meta_link', l($tag->name, 'taxonomy/term/'. $tag->tid));
|
| 99 |
}
|
| 100 |
$versions = implode(', ', $links);
|
| 101 |
}
|
| 102 |
/*
|
| 103 |
if (count($taxonomy[DRUPALORG_HANDBOOK_VERSION_VID])) {
|
| 104 |
// @todo Adjust DRUPALORG_HANDBOOK_VERSION_VID to the vid of the new feature vocabulary
|
| 105 |
$links = array();
|
| 106 |
foreach ($taxonomy[DRUPALORG_HANDBOOK_VERSION_VID] as $tag) {
|
| 107 |
$links[] = theme('drupalorg_handbook_meta_link', l($tag->name, 'taxonomy/term/'. $tag->tid));
|
| 108 |
}
|
| 109 |
$features = implode(', ', $links);
|
| 110 |
}
|
| 111 |
*/
|
| 112 |
}
|
| 113 |
|
| 114 |
// Set the modified date for display.
|
| 115 |
// @todo Look into better localized date formats, whenever we have locales.
|
| 116 |
$modified = format_date($node->changed, 'custom', 'F j, Y');
|
| 117 |
|
| 118 |
// Collect a list of people who have edited the book page ever.
|
| 119 |
// removing authors for now per issue http://drupal.org/node/428410
|
| 120 |
/*$authors_query = db_query("SELECT u.uid, u.name FROM {node_revisions} nr JOIN {users} u ON nr.uid = u.uid WHERE nid = %d GROUP BY u.uid ORDER BY timestamp", $node->nid);
|
| 121 |
while ($author = db_fetch_object($authors_query)) {
|
| 122 |
$author_links[] = theme('drupalorg_handbook_meta_link', l($author->name, 'user/'. $author->uid));
|
| 123 |
}
|
| 124 |
$authors = implode(', ', $author_links);*/
|
| 125 |
$authors = NULL;
|
| 126 |
|
| 127 |
// @todo Add language listings when available.
|
| 128 |
$languages = NULL;
|
| 129 |
// @todo Add alerts (discussion pages) when available.
|
| 130 |
$alerts = NULL;
|
| 131 |
|
| 132 |
return theme('drupalorg_handbook_meta_data', $versions, $features, $modified, $authors, $languages, $alerts);
|
| 133 |
}
|
| 134 |
|
| 135 |
/**
|
| 136 |
* List handbook pages with comments in descending comment order.
|
| 137 |
*/
|
| 138 |
function drupalorg_handbook_with_comments() {
|
| 139 |
$header = array(
|
| 140 |
array('data' => 'Page'),
|
| 141 |
array('data' => 'Comments', 'field' => 'comment_count', 'sort' => 'desc'),
|
| 142 |
array('data' => 'Last comment', 'field' => 'last_comment_timestamp'),
|
| 143 |
);
|
| 144 |
|
| 145 |
$result = db_query("SELECT n.nid, n.title, s.comment_count, s.last_comment_timestamp
|
| 146 |
FROM {node} n LEFT JOIN {node_comment_statistics} s ON n.nid = s.nid
|
| 147 |
WHERE n.type = 'book' AND s.comment_count >= 1 AND n.status = 1" . tablesort_sql($header));
|
| 148 |
|
| 149 |
while ($node = db_fetch_object($result)) {
|
| 150 |
$rows[] = array(l($node->title, "node/$node->nid"), $node->comment_count, format_interval(time() - $node->last_comment_timestamp) .' ago');
|
| 151 |
}
|
| 152 |
|
| 153 |
return theme('table', $header, $rows);
|
| 154 |
}
|
| 155 |
|
| 156 |
/**
|
| 157 |
* List pages ordered by visit number descending order.
|
| 158 |
*/
|
| 159 |
function drupalorg_handbook_most_popular() {
|
| 160 |
$header = array('Rank', 'Page', 'Visits today', 'Total visits');
|
| 161 |
|
| 162 |
$result = db_query("SELECT n.nid, n.title, c.daycount, c.totalcount
|
| 163 |
FROM {node} n INNER JOIN {node_counter} c ON n.nid = c.nid
|
| 164 |
WHERE n.type = 'book' AND n.status = 1
|
| 165 |
ORDER BY c.daycount DESC
|
| 166 |
LIMIT 0, 100");
|
| 167 |
while ($node = db_fetch_object($result)) {
|
| 168 |
$rows[] = array(++$rank, l($node->title, "node/$node->nid"), $node->daycount, $node->totalcount);
|
| 169 |
}
|
| 170 |
|
| 171 |
return theme('table', $header, $rows);
|
| 172 |
}
|
| 173 |
|
| 174 |
/**
|
| 175 |
* List usernames with site maintainer role.
|
| 176 |
*/
|
| 177 |
function drupalorg_handbook_site_maintainers() {
|
| 178 |
$output = 'If you have been around for a while, and you want to help maintain Drupal.org and are willing to accept the added responsibilities that come with it, sign up on the <a href="http://drupal.org/mailing-lists">Infrastructure team</a> list.';
|
| 179 |
$output .= '<ol>';
|
| 180 |
$result = db_query("SELECT DISTINCT(u.uid), u.name
|
| 181 |
FROM {users} u INNER JOIN {users_roles} r ON u.uid = r.uid
|
| 182 |
WHERE r.rid = 3 OR r.rid = 4 OR r.rid = 9
|
| 183 |
ORDER BY u.name "); // 3 = site maintainer, 4 = administrator
|
| 184 |
while ($account = db_fetch_object($result)) {
|
| 185 |
$output .= "<li>". theme('username', $account) ."</li>";
|
| 186 |
}
|
| 187 |
$output .= '</ol>';
|
| 188 |
return $output;
|
| 189 |
}
|
| 190 |
|
| 191 |
/**
|
| 192 |
* List usernames with handbook maintainer role.
|
| 193 |
*/
|
| 194 |
function drupalorg_handbook_maintainers() {
|
| 195 |
$output = '<p>If you are interested in helping maintain/update/correct the documentation on Drupal.org, read up on the <a href="http://drupal.org/node/24572">many ways to get involved</a>.</p>';
|
| 196 |
$output .= '<p>Note: Many of our <a href="/site-maintainers">site maintainers</a> also participate on the documentation team and are not listed here. Due to their broader responsibilities on drupal.org, site maintainers are listed separately.</p>';
|
| 197 |
$output .= '<ol>';
|
| 198 |
$result = db_query("SELECT DISTINCT(u.uid), u.name
|
| 199 |
FROM {users} u INNER JOIN {users_roles} r ON u.uid = r.uid
|
| 200 |
WHERE r.rid = 5 OR r.rid = 9
|
| 201 |
ORDER BY u.name "); // 3 = site maintainer, 4 = administrator 5 = document maintainer
|
| 202 |
while ($account = db_fetch_object($result)) {
|
| 203 |
$output .= "<li>". theme('username', $account) ."</li>";
|
| 204 |
}
|
| 205 |
$output .= '</ol>';
|
| 206 |
return $output;
|
| 207 |
}
|
| 208 |
|
| 209 |
/**
|
| 210 |
* Count and list contributors to the books.
|
| 211 |
*/
|
| 212 |
function drupalorg_handbook_book_contributors() {
|
| 213 |
$result = db_query("SELECT u.uid, u.name, COUNT(n.nid) AS pages
|
| 214 |
FROM {node} n INNER JOIN {users} u ON n.uid = u.uid
|
| 215 |
WHERE n.type = 'book' AND n.status = 1 AND n.moderate = 0
|
| 216 |
GROUP BY u.name
|
| 217 |
ORDER BY pages DESC");
|
| 218 |
$output .= "<ul>";
|
| 219 |
while ($contributor = db_fetch_object($result)) {
|
| 220 |
$output .= "<li>". theme('username', $contributor) ." (". format_plural($contributor->pages, "@count page", "@count pages") .")</li>";
|
| 221 |
}
|
| 222 |
$output .= "</ul>";
|
| 223 |
return $output;
|
| 224 |
}
|
| 225 |
|
| 226 |
/**
|
| 227 |
* Collect and cache revision maintainers (those who made revisions to books).
|
| 228 |
*/
|
| 229 |
function drupalorg_handbook_revision_maintainers() {
|
| 230 |
if ($cache = cache_get('node_263594')) {
|
| 231 |
return $cache->data;
|
| 232 |
}
|
| 233 |
|
| 234 |
$result = db_query("SELECT u.uid, u.name, COUNT(nr.vid) AS pages
|
| 235 |
FROM {node} n INNER JOIN {node_revisions} nr ON n.nid = nr.nid INNER JOIN {users} u ON u.uid = n.uid
|
| 236 |
WHERE n.type = 'book' AND n.status = 1 AND n.moderate = 0
|
| 237 |
GROUP BY u.name
|
| 238 |
ORDER BY pages DESC");
|
| 239 |
|
| 240 |
$list = array();
|
| 241 |
while ($contributor = db_fetch_object($result)) {
|
| 242 |
$list[] = theme('username', $contributor) ." (". format_plural($contributor->pages, "@count revision", "@count revisions") .")";
|
| 243 |
}
|
| 244 |
|
| 245 |
$output = theme('item_list', $list);
|
| 246 |
cache_set('node_263594', $output, 'cache', 300);
|
| 247 |
return $output;
|
| 248 |
}
|
| 249 |
|
| 250 |
/**
|
| 251 |
* API function to get recent update information for the documentation pages.
|
| 252 |
*/
|
| 253 |
function drupalorg_handbook_get_recent_updates($limit = 50) {
|
| 254 |
return db_query_range("SELECT n.nid, n.title, n.changed, r.log, u.uid, u.name
|
| 255 |
FROM {node} n
|
| 256 |
INNER JOIN {book} b ON n.nid = b.nid INNER JOIN {node_revisions} r ON n.vid = r.vid INNER JOIN {users} u ON u.uid = r.uid
|
| 257 |
WHERE n.status = 1
|
| 258 |
ORDER BY n.changed DESC", 0, $limit);
|
| 259 |
}
|
| 260 |
|
| 261 |
/**
|
| 262 |
* List most recent updates to book pages for moderation reasons.
|
| 263 |
*/
|
| 264 |
function drupalorg_handbook_display_recent_updates() {
|
| 265 |
$result = drupalorg_handbook_get_recent_updates(50);
|
| 266 |
while ($page = db_fetch_object($result)) {
|
| 267 |
$rows[] = array(
|
| 268 |
l($page->title, "node/$page->nid") .' '. theme('mark', node_mark($page->nid, $page->changed)) . ($page->log ? "<br />". check_plain($page->log) : ''),
|
| 269 |
theme('username', $page),
|
| 270 |
t('%time ago', array('%time' => format_interval(time() - $page->changed))),
|
| 271 |
l(t('Diff'), "node/$page->nid/revisions/view/latest"),
|
| 272 |
);
|
| 273 |
}
|
| 274 |
$header = array('Page', 'Edited', 'Updated', 'Diff');
|
| 275 |
|
| 276 |
$output = theme('table', $header, $rows);
|
| 277 |
return $output;
|
| 278 |
}
|
| 279 |
|
| 280 |
/**
|
| 281 |
* License and quick links blocks for docs.
|
| 282 |
*/
|
| 283 |
function drupalorg_handbook_block($op = 'list', $delta = 0, $edit = array()) {
|
| 284 |
switch ($op) {
|
| 285 |
case 'list':
|
| 286 |
$blocks[0]['info'] = t('Handbook License');
|
| 287 |
$blocks[1]['info'] = t('Documentation Quick Links');
|
| 288 |
return $blocks;
|
| 289 |
|
| 290 |
case 'view':
|
| 291 |
switch($delta) {
|
| 292 |
case 0:
|
| 293 |
if (drupalorg_handbook_block_is_visible()) {
|
| 294 |
$block['subject'] = t('Handbook license');
|
| 295 |
$block['content'] = drupalorg_handbook_license();
|
| 296 |
return $block;
|
| 297 |
}
|
| 298 |
|
| 299 |
case 1:
|
| 300 |
if (drupalorg_handbook_block_is_visible()) {
|
| 301 |
$block['subject'] = t('Documentation Quick Links');
|
| 302 |
$block['content'] = drupalorg_handbook_doc_quick_links();
|
| 303 |
return $block;
|
| 304 |
}
|
| 305 |
}
|
| 306 |
}
|
| 307 |
}
|
| 308 |
|
| 309 |
/**
|
| 310 |
* Check whether the current node is a block module.
|
| 311 |
*
|
| 312 |
* Based on Drupal 6 code from book_block().
|
| 313 |
*/
|
| 314 |
function drupalorg_handbook_block_is_visible() {
|
| 315 |
return ($node = menu_get_object()) && !empty($node->book['bid']);
|
| 316 |
}
|
| 317 |
|
| 318 |
/**
|
| 319 |
* Quick links on handbook pages.
|
| 320 |
*/
|
| 321 |
function drupalorg_handbook_doc_quick_links() {
|
| 322 |
$output = <<<EOT
|
| 323 |
<ul>
|
| 324 |
<li><a href="/handbook/modules">Module documentation</a></li>
|
| 325 |
<li><a href="/node/257">Customization and theming</a></li>
|
| 326 |
<li><a href="/search/node">Search drupal.org</a></li>
|
| 327 |
<li><a href="/forum/0">Support forums</a></li>
|
| 328 |
<li><a href="/handbook">View other handbooks</a></li>
|
| 329 |
<li><a href="/project/issues/documentation">Suggest documentation improvements</a></li>
|
| 330 |
<li><a href="/node/23367">Join the doc team</a></li>
|
| 331 |
</ul>
|
| 332 |
EOT;
|
| 333 |
return $output;
|
| 334 |
}
|
| 335 |
|
| 336 |
/**
|
| 337 |
* License block for the handbook pages.
|
| 338 |
*/
|
| 339 |
function drupalorg_handbook_license() {
|
| 340 |
return t('The Drupal handbook pages are © 2000-!year by the <a href="@contributors_link">individual contributors</a> and can be used in accordance with the <a href="@ccl_url">Creative Commons License, Attribution-ShareAlike 2.0</a>. PHP code is distributed under the <a href="@gpl_url">GNU General Public License</a>', array('!year' => date('Y'), '@contributors_link' => url('node/14205'), '@ccl_url' => url('node/14307'), '@gpl_url' => url('http://www.gnu.org/licenses/old-licenses/gpl-2.0.html')));
|
| 341 |
}
|
| 342 |
|
| 343 |
/**
|
| 344 |
* Implementation of hook_theme().
|
| 345 |
*/
|
| 346 |
function drupalorg_handbook_theme($existing, $type, $theme, $path) {
|
| 347 |
return array(
|
| 348 |
'drupalorg_handbook_meta_data' => array(
|
| 349 |
'arguments' => array('versions' => NULL, 'features' => NULL, 'modified' => NULL, 'authors' => NULL, 'languages' => NULL, 'alerts' => NULL),
|
| 350 |
'template' => 'handbook-meta-data',
|
| 351 |
),
|
| 352 |
'drupalorg_handbook_meta_link' => array(
|
| 353 |
'arguments' => array('link' => NULL),
|
| 354 |
),
|
| 355 |
);
|
| 356 |
}
|
| 357 |
|
| 358 |
/**
|
| 359 |
* Simply wraps links in a span to make button theming easier.
|
| 360 |
*
|
| 361 |
* @param $link
|
| 362 |
* An already generated HTML link, from l() for example.
|
| 363 |
* @return
|
| 364 |
* The link wrapped in spans to make it possible to use varying width buttons.
|
| 365 |
*/
|
| 366 |
function theme_drupalorg_handbook_meta_link($link) {
|
| 367 |
return '<span>'. $link .'</span>';
|
| 368 |
}
|
| 369 |
|
| 370 |
/**
|
| 371 |
* Implementation of template_preprocess_drupalorg_home().
|
| 372 |
*
|
| 373 |
* @todo
|
| 374 |
* Add caching.
|
| 375 |
*/
|
| 376 |
function drupalorg_handbook_preprocess_drupalorg_home(&$vars) {
|
| 377 |
$result = drupalorg_handbook_get_recent_updates(5);
|
| 378 |
$recent_updates = '';
|
| 379 |
while ($node = db_fetch_object($result)) {
|
| 380 |
// Fake the created time for the theme function, so we can easily theme it.
|
| 381 |
$node->created = $node->changed;
|
| 382 |
$recent_updates .= '<h6>'. l($node->title, 'node/'. $node->nid) .'</h6><p class="submitted">'. theme('node_submitted', $node) .'</p>';
|
| 383 |
}
|
| 384 |
// We have no place to link this to in a nice way.
|
| 385 |
// $fresh_news .= '<p>'. l(t('More documentation updates...'), '...') .'</p>';
|
| 386 |
$vars['tab_content_docs'] = $recent_updates;
|
| 387 |
}
|