'. t('Introduction') .''; $html .= '

'. t('Hall of Fame (HOF) offers a concise summary of site statistics on a read-only basis. Unlike the built-in statistical features of Drupal itself, HOF is intended for public consumption rather than [mostly] for administrators. The module can display its output in sections such as "Most Popular Content", "Most Active Contributors", etc., or it can make a single large page with the combined output of all sections, or it can produce a sparse table-of-contents page with links to each section.'); $html .= '

'. t('Administrators can determine what roles have permission to view the HOF pages, who can change the module\'s settings, and some behaviors of the module (such as which output section is the default).'); $html .= '

'. t('HOF is aware of the weblink and scheduler modules, and if they are installed, it adds specific reporting features related to these, as explained below.'); $html .= '

'. t('Statistical Computations') .'

'; $html .= '

'. t('Overall statistics include, among others:') .'

'; $html .= '
'. t('Date of first publication, and time since that date') .'
'. t('Computed by looking for the earliest creation date of any node'); $html .= '
'. t('Number of active user accounts') .'
'. t('Computed by looking for an update date greater than the creation date of the account, an attempt to ignore accounts which are created but never used.'); $html .= '
'. t('Weblinks in directory') .'
'. t('If the weblink module is installed, this statistic reports how many weblinks have been published.'); $html .= '
'. t('Items in queue awaiting approval') .'
'. t('Tallies all unpublished nodes. If the scheduler module is installe, nodes that have been scheduled for future publication are not counted in this number.'); $html .= '
'. t('Items awaiting scheduled release') .'
'. t('If the scheduler module is installed, this statistic indicates how many nodes have been scheduled for release in the future.'); $html .= '

'. t('Other statistics are computed for the past week, the past month, the past year, and for all time since the site\'s creation. These include:') .'

'; $html .= '
'. t('Most active contributors') .'
'. t('Computed by looking at the user who created each node.'); $html .= '
'. t('Most popular content') .'
'. t('Computed by tallying the number of reads for each node.'); $html .= '
'. t('Stories published') .'
'. t('In this context, "stories" really means "promoted nodes", the concept being intended as "things that are announced on the default main page as newsworthy" as opposed to regular pages that may include static-ish content, terms of service, privacy policy, and so on.'); $html .= '
'. t('Total articles published') .'
'. t('This is simply a count of all published nodes on the site, regardless of type.'); $html .= '

'; return $html; } } /** * Reports the permissions exposed by this module */ function hof_perm() { $sections = hof_get_sections(); $enabled = variable_get('hof_enable_sections', $sections); unset($sections['toc']); unset($sections['all']); $perms = array('access hof', 'administer hof'); foreach ($sections as $section=>$title) { if (array_search($section, $enabled) !== FALSE) { $menu_title = strtolower(preg_replace('/\W+/', ' ', $title)); $perm = 'access hof '. $menu_title; $perms[$perm] = array('title'=>$title, 'description'=>t('View this page of the Hall of Fame')); } } return $perms; } /** * Builds the HOF settings form */ function hof_settings_form() { $sections = hof_get_sections(); unset($sections['toc']); // These two are always enabled unset($sections['all']); $sections2 = hof_get_sections(); // For selecting default display, do not remove toc and all $form = array(); $form['hof_sections'] = array( '#type' => 'fieldset', '#tree' => false, '#title' => t('Hall of Fame Sections'), '#collapsible' => true, ); $form['hof_sections']['hof_enable_sections'] = array( '#type' => 'checkboxes', '#title' => t('Enable HOF sections'), '#description' => t('Check or uncheck each option to enable or disable that section of the Hall of Fame'), '#default_value' => variable_get('hof_enable_sections', $sections), '#options' => $sections, ); $sections = hof_get_sections(); unset($sections['default']); // Not applicable here $form['hof_sections']['hof_default_section'] = array( '#type' => 'select', '#title' => t('Default HOF section'), '#description' => t('Select the page that will display at the top-level Hall of Fame URL'), '#default_value' => variable_get('hof_default_section', 'all'), '#options' => $sections2, '#multiple' => false, ); $form['hof_toc'] = array( '#type' => 'fieldset', '#tree' => false, '#title' => t('Table of Contents (TOC) Settings'), '#collapsible' => true, ); $form['hof_toc']['hof_toc_position'] = array( '#type' => 'select', '#title' => t('TOC position'), '#description' => t('Select the position of a table-of-contents, or link to the table of contents, to display on all pages except for the TOC page itself.'), '#default_value' => variable_get('hof_toc_position', 'bottom'), '#options' => array('top' => t('Above report'), 'bottom' => t('Below report'), 'none' => t('No automatic TOC')), '#multiple' => false, ); $form['hof_toc']['hof_toc_mode'] = array( '#type' => 'select', '#title' => t('TOC mode'), '#description' => t('Should the embedded table-of-contents be just a link to the TOC page, or should all the links to individual pages be listed? This affects only the TOC embedded in other pages, not the TOC on its own dedicated page.'), '#default_value' => variable_get('hof_toc_mode', 'link'), '#options' => array('link' => t('Link to TOC page'), 'full' => t('Show full TOC in pages')), '#multiple' => false, ); $form['hof_toc']['hof_toc_in_toc'] = array( '#type' => 'checkbox', '#title' => t('Include HOF TOC in embedded TOC'), '#description' => t('If the embedded table-of-contents is enabled, this setting controls whether that TOC will include a link to the full-page TOC. You can disable that link if it is redundant because of your choice of theme and other Hall of Fame settings.'), '#default_value' => variable_get('hof_toc_in_toc', TRUE), '#return_value' => 1, ); $form['hof_reports'] = array( '#type' => 'fieldset', '#tree' => false, '#title' => t('Report Settings'), '#collapsible' => true, ); $numbers = array('3' => 3, '5' => 5, '10' => 10, '15' => 15, '20' => 20); $form['hof_reports']['hof_active_user_count'] = array( '#type' => 'select', '#title' => t('Users Per Activity Period'), '#description' => t('For reports where the most-active users are listed for given time periods, this setting determines how many users will be listed (maximum) for each time period. The actual displayed list may be shorter if not enough users qualify to be listed.'), '#default_value' => variable_get('hof_active_user_count', 5), '#options' => $numbers, '#multiple' => false, ); $form['hof_reports']['hof_item_count'] = array( '#type' => 'select', '#title' => t('Items Per Activity Period'), '#description' => t('For reports where items are ranked (such as by popularity) for given time periods, this setting determines how many items will be listed (maximum) for each time period. The actual displayed list may be shorter if not enough items qualify to be listed.'), '#default_value' => variable_get('hof_item_count', 5), '#options' => $numbers, '#multiple' => false, ); $form['hof_reports']['hof_promoted_label'] = array( '#type' => 'textfield', '#title' => t('Label for promoted articles'), '#description' => t('When reporting statistics for promoted nodes (articles), this is the user-friendly title that will be used to describe these nodes. It should be plural and should be formatted with appropriate capitalization for use as a title. Suggested examples would be "Promoted Articles" (the default), "News Items", and so forth. This option should be decided based on your site\'s policy for promoting content.'), '#default_value' => variable_get('hof_promoted_label', t('Promoted Articles')), '#size' => 30, '#maxlength' => 40, '#required' => true, ); $form['hof_reports']['hof_node_label'] = array( '#type' => 'textfield', '#title' => t('Alternative for "nodes"'), '#description' => t('When reporting statistics for all nodes, this is the user-friendly term that is used in place of Drupal\'s internal term, "nodes". It should be plural and lower-case. Suggested examples would be "articles" (the default), "pages", "content items", and so forth.'), '#default_value' => variable_get('hof_node_label', t('articles')), '#size' => 20, '#maxlength' => 20, '#required' => true, ); $form['hof_reports']['hof_nodes_include_images'] = array( '#type' => 'checkbox', '#title' => t('Count images as regular content'), '#description' => t('Images are usually uploaded into collections or albums, and many site administrators do not think of each image as a content page in its own right. Technically, however, images are "nodes" within Drupal just like any other content. This setting controls whether they are counted as such in the Hall of Fame statistics for articles. If enabled, images are counted as articles; if disabled, they are counted only in their own separate tally.'), '#default_value' => variable_get('hof_nodes_include_images', 0), '#return_value' => 1, ); $form['hof_reports']['hof_site_create_date_manual'] = array( '#type' => 'checkbox', '#title' => t('Set first online date manually'), '#description' => t('If you set this option, the date below will display as the site\'s initial online date in the Site Statistics section. If this option is disabled, then the first published content will establish the site\'s starting date.'), '#default_value' => variable_get('hof_site_create_date_manual', FALSE), ); $form['hof_reports']['hof_site_create_date'] = array( '#type' => 'date', '#title' => t('Site first online date'), '#default_value' => variable_get('hof_site_create_date', null), '#description' => t('This date is used only if the checkbox above is set. This is interpreted as your site\'s default timezone.'), ); return system_settings_form($form); } /** * Adds appropriate entries to the Drupal menu */ function hof_menu() { $items = array(); $items['hof'] = array( 'title' => t('Hall of Fame'), 'page callback' => 'hof_page', 'access' => 'user_access', 'access arguments' => array('access hof'), 'type' => MENU_CALLBACK, ); $sections = hof_get_sections(); foreach ($sections as $section=>$title) { $weight = ($section == 'all') ? 10 : 0; $items['hof/'. $section] = array( 'title' => $title, 'page callback' => 'hof_page', 'type' => MENU_CALLBACK, 'access callback' => 'hof_access', 'access arguments' => array($section), 'weight' => $weight, ); } $items['admin/settings/hof'] = array( 'title' => t('Hall of Fame'), 'description' => t('Control the display of the Hall of Fame'), 'page callback' => 'drupal_get_form', 'page arguments' => array('hof_settings_form'), 'access callback' => 'user_access', 'access arguments' => array('administer hof'), 'type' => MENU_NORMAL_ITEM, ); return $items; } function hof_access($section) { $sections = hof_get_sections(); if ($section == 'default' || $section == 'all' || $section == 'toc') { $acc = user_access('access hof'); } else { $title = $sections[$section]; $section_access_key = 'access hof '. strtolower(preg_replace('/\W+/', ' ', $title)); $acc1 = user_access('access hof'); $acc2 = user_access($section_access_key); $acc = $acc1 && $acc2; } return $acc; } /** * Enumerates, as an associative array, the sections and their (translatable) titles */ function hof_get_sections() { $sections = array( 'site' => t('Site Statistics'), 'bytype' => t('Content Published by Type'), 'contrib' => t('Most Active Contributors'), 'node' => t('Most Popular Content'), 'comment' => t('Most Active Commentors'), 'files' => t('Uploaded Files'), 'all' => t('All Statistics'), 'toc' => t('HOF Table of Contents') ); return $sections; } /** * Enumerates sections as an associative array, but only those that are enabled by * the site administrator and which are accessible to the current user. The sections * 'default', 'toc', and 'all' are enabled for anyone who has access to the HOF * module at all. */ function hof_get_allowed_sections() { // Trivial rejection if user has no access at all if (! user_access('access hof')) { return array(); } $sections = hof_get_sections(); $enabled = variable_get('hof_enable_sections', $sections); foreach ($sections as $section=>$title) { if ($section != 'toc' && $section != 'all' && $section != 'default') { /* $title = strtolower(preg_replace('/\W+/', ' ', $title)); $perm = 'access hof '. $title; if (! $enabled[$section]) { unset($sections[$section]); } if (! user_access($perm)) { unset($sections[$section]); } */ if (! $enabled[$section] || ! hof_access($section)) { unset($sections[$section]); } } } return $sections; } /** * Builds some global arrays for time interval handling */ function _hof_constants() { // Compute some time intervals (seconds before now) $GLOBALS['hof_last_week'] = strtotime("-1 week"); $GLOBALS['hof_last_month'] = strtotime("-1 month"); $GLOBALS['hof_last_year'] = strtotime("-1 year"); $GLOBALS['hof_intervals'] = array( t('week') => $GLOBALS['hof_last_week'], t('month') => $GLOBALS['hof_last_month'], t('year') => $GLOBALS['hof_last_year'], t('epoch') => 0 ); } /** * Handle the display of pages */ function hof_page() { _hof_constants(); $toc_position = variable_get('hof_toc_position', 'none'); $default_section = variable_get('hof_default_section', 'toc'); $html = '
'; $section = arg(1); $section = empty($section) ? 'default' : $section; if ($section == 'toc' || ($default_section == 'toc' && $section == 'default')) { $need_toc = FALSE; } else { $need_toc = TRUE; } $function = '_hof_page_section_'. $section; if (function_exists($function)) { if ($need_toc) { $sections = hof_get_sections(); if (isset($sections[$section])) { drupal_set_title(t('Hall of Fame: '). $sections[$section]); } else { drupal_set_title(t('Hall of Fame')); } if ($toc_position == 'top') { $html .= _hof_embedded_toc(); } } $args = func_get_args(); if (is_array($args)) { $args = array_shift($args); } $html .= "

". t('Statistics for ') . variable_get('site_name', t('this site')) . t(' as of ') . format_date(REQUEST_TIME, "long") ."

\n"; $html .= call_user_func_array($function, $args); if ($need_toc) { if ($toc_position == 'bottom') { $html .= _hof_embedded_toc(); } } $html .= '
'; return $html; } else { drupal_not_found(); } } /** * Default HOF page, depending on admin settings */ function _hof_page_section_default() { $default_section = variable_get('hof_default_section', 'toc'); $function = '_hof_page_section_'. $default_section; if (function_exists($function)) { $args = func_get_args(); return call_user_func_array($function, $args); } else { drupal_set_message('Internal error: HOF tried to call function '. $function); return "Internal error"; } } /** * TOC only, providing a list of the other pages. * This is the full TOC, not the embedded one. */ function _hof_page_section_toc() { $html = ""; $sections = hof_get_allowed_sections(); unset($sections['toc']); foreach ($sections as $section=>$title) { $function = '_hof_page_section_'. $section; if ($section != 'toc' && $section != 'default' && function_exists($function)) { $html .= "\n

". l($title, 'hof/'. $section) ."

\n"; } } return $html; } /** * TOC for embedding in pages, format depending * on the administrative settings of the module. */ function _hof_embedded_toc() { $sections = hof_get_allowed_sections(); if (! variable_get('hof_toc_in_toc', TRUE)) { unset($sections['toc']); } $mode = variable_get('hof_toc_mode', 'link'); $html = '

'; switch ($mode) { case 'full': $links = array(); foreach ($sections as $section=>$title) { $links[] = l($title, 'hof/'. $section); } $html .= '[ '. implode(' | ', $links) .' ]'; break; default: $html .= '[ '. l(t('Hall of Fame Table of Contents'), 'hof/toc') .' ]'; } $html .= "

\n"; return $html; } /** * All statistics (combines all others) */ function _hof_page_section_all() { $html = ""; $sections = hof_get_allowed_sections(); unset($sections['toc']); // Don't display the TOC as part of the "all" display foreach ($sections as $section=>$title) { $function = '_hof_page_section_'. $section; if ($section != 'all' && $section != 'default' && function_exists($function)) { $html .= "\n

". l($title, 'hof/'. $section) ."

\n"; $html .= call_user_func($function); } } return $html; } /** * Site statistics (general summary) */ function _hof_page_section_site() { if (variable_get('hof_site_create_date_manual', FALSE)) { // Get the manually-set date $ymd = variable_get('hof_site_create_date', array()); $first_tstmp = gmmktime(0, 0, 0, $ymd['month'], $ymd['day'], $ymd['year'], FALSE) - variable_get('date_default_timezone', 0); } else { $sql="SELECT MIN(created) AS began FROM {node} WHERE status=1 AND moderate=0"; $first_tstmp = db_result(db_query($sql)); } $founded = format_date($first_tstmp, "long"); $duration = format_interval(REQUEST_TIME - $first_tstmp, 3); $html = "

\n"; $html .= '

' . $image_text . '

'; return $html; } /** * Node statistics by type */ function _hof_page_section_bytype() { $html = ''; $intervals = $GLOBALS['hof_intervals']; $sql = "SELECT DISTINCT n.type, nt.name, count(*) AS quantity, sum(totalcount) AS total_reads FROM {node} n LEFT JOIN {node_type} nt ON n.type=nt.type LEFT JOIN {node_counter} nc ON (n.nid=nc.nid OR nc.nid IS NULL) WHERE n.status=1 AND n.changed>=%d GROUP BY type ORDER BY type"; foreach ($intervals as $interval=>$tstmp) { if ($interval == t('epoch')) { $label = t("For All Time"); } else { $label = t("For the past ") . $interval; } $html .= '

' . $label . '

'; $rows = array(); $result = db_query($sql, $tstmp); if ($result) { while ($row = db_fetch_array($result)) { $type_name = empty($row['name']) ? $row['type'] : $row['name']; $rows[] = array('data'=>array(t($type_name), intval($row['quantity']), intval($row['total_reads'])), 'align'=>'right'); } $headers = array(t('Content Type'),t('Total Published'),t('Total Reads')); // In the near future, will make this themeable, but need to do the whole // module at once for aesthetic reasons. // $html .= theme('table',$headers,$rows, array('width'=>'80%', 'class'=>'hof')); // For now, use the legacy approach. $html .= ''; foreach ($headers as $hdr) { $html .= ''; } $html .= ''; foreach ($rows as $row) { $html .= ''; } $html .= '
' . htmlspecialchars($hdr) . '
' . htmlspecialchars($row['data'][0]) . '' . $row['data'][1] . '' . $row['data'][2] . '
'; } } $html .= '

 

'; return $html; } /** * Contributor statistics (most active) */ function _hof_page_section_contrib() { $sql = "SELECT count(*) contribs, name, n.uid FROM {node} n left join {users} u on n.uid=u.uid where n.uid>0 and n.status=1 and n.moderate=0 and n.created>=%d"; return _hof_most_active($sql); } /** * Comment statistics (most active) */ function _hof_page_section_comment() { $sql = "SELECT count(*) contribs, u.name, u.uid FROM {comment} c left join {users} u on c.uid=u.uid where u.uid is not null and c.uid>0 and c.timestamp>=%d"; return _hof_most_active($sql); } /** * Analyzes the "most active (something)" and creates HTML with the results. * The parameter is an SQL statement that must return exactly three columns, * in this order and with these aliases or names: * contribs The integer count of activities for the given user * name The name of the user being reported * uid The uid of the user being reported * * The SQL must also contain a decimal placeholder %d which will be replaced with * the timestamp (integer seconds-past-epoch) for the earliest applicable time * for each iteration of the query. * * The SQL will be appended with appropriate GROUP BY, HAVING, ORDER, and LIMIT * clauses based on administrative settings. */ function _hof_most_active($sql) { $active_limit = variable_get('hof_active_user_count', 5); $sql_append = " GROUP BY name HAVING contribs>1 ORDER BY contribs DESC "; $sql_full = $sql . $sql_append; $tops = array(); // Do the interval calculations $tops = array(); $intervals = $GLOBALS['hof_intervals']; reset($intervals); while (list($interval, $tstmp) = each($intervals)) { $tops[$interval] = array(); $result = db_query_range($sql_full, $intervals[$interval], 0,$active_limit); while ($row = db_fetch_array($result)) { $tops[$interval][] = $row; } } $html = "

\n"; reset($intervals); while (list($interval, $tstmp) = each($intervals)) { $html .= "\n"; if ($interval == t('epoch')) { $html .= "\n"; } else { $html .= "\n"; } foreach ($tops[$interval] as $list) { $userlink = l($list['name'], 'user/'.$list['uid']); $html .= "\n"; } } $html .= "
 
" . t("For All Time") . "
" . t("For the past ") . $interval . "
" . $userlink . "" . $list['contribs'] . t(" items") . "
\n"; return $html; } /** * Node statistics for uploaded files */ function _hof_page_section_files() { if (! module_exists('upload')) { return "

" . t('The upload module is not enabled, so this Hall of Fame section is not applicable to this site.') . '

'; } $html = "

\n"; $intervals = $GLOBALS['hof_intervals']; reset($intervals); if (variable_get('hof_nodes_include_images', 0)) { $sql_cond = ''; $message = '%filecount files and images uploaded'; } else { $sql_cond = ' AND f.filemime NOT LIKE \'image/%\''; $message = '%filecount files uploaded'; } while (list($interval, $tstmp) = each($intervals)) { $sql = "SELECT COUNT(DISTINCT u.fid, u.nid) AS filecount FROM {node} n, {upload} u, {files} f WHERE n.nid=u.nid AND n.vid=u.vid AND f.fid=u.fid AND n.created>=" . $tstmp . " AND n.status=1 AND moderate=0"; $sql .= $sql_cond; $result = db_query($sql); if ($row = db_fetch_array($result)) { if ($interval == t('epoch')) { $html .= "\n"; } else { $html .= "\n"; } $html .= "\n"; } } $html .= "
" . t("For All Time") . "
" . t("For the past ") . $interval . "
" . t($message, array('%filecount'=>$row['filecount'])) . "
\n"; return $html; } /** * Node statistics (most popular) */ function _hof_page_section_node() { if (! module_exists('statistics')) { $html = t('

The statistics module is not installed or is disabled. Read counts are not available unless this is corrected.

'); return $html; } if (! variable_get('statistics_count_content_views', FALSE)) { $html = t('

The statistics module is installed, but read counts are not enabled. Correct this in the Access Log Settings page.

'); return $html; } $popular_limit = variable_get('hof_item_count', 5); // Do the interval calculations $pops = array(); $intervals = $GLOBALS['hof_intervals']; reset($intervals); while (list($interval, $tstmp) = each($intervals)) { $pops[$interval] = array(); $sql = "SELECT n.nid, n.title, c.totalcount FROM {node} n left join {node_counter} c on n.nid=c.nid where n.status=1 and n.moderate=0 and n.created>=" . $intervals[$interval] . " and c.totalcount>1 and title not like '%page not found%' order by c.totalcount desc, n.created desc limit " . $popular_limit; $result = db_query($sql); while ($row = db_fetch_array($result)) { $pops[$interval][] = $row; } } $html = "

\n"; reset($intervals); while (list($interval, $tstmp) = each($intervals)) { $html .= "\n"; if ($interval == t('epoch')) { $html .= "\n"; } else { $html .= "\n"; } foreach ($pops[$interval] as $list) { $nodelink = l($list['title'], 'node/' . $list['nid']); $html .= "\n"; } } $html .= "
 
" . t("For All Time") . "
" . t("For the past ") . $interval . "
" . $nodelink . "" . $list['totalcount'] . " " . t("reads") . "
\n"; return $html; }