Index: project_usage.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project/usage/project_usage.install,v retrieving revision 1.1 diff -u -p -u -r1.1 project_usage.install --- project_usage.install 7 Aug 2007 20:21:33 -0000 1.1 +++ project_usage.install 19 Aug 2007 20:08:07 -0000 @@ -38,6 +38,15 @@ function project_usage_install() { count int unsigned NOT NULL default '0', PRIMARY KEY (nid, timestamp) ) /*!40100 DEFAULT CHARACTER SET utf8 */;"); + db_query("CREATE TABLE IF NOT EXISTS {cache_project_usage} ( + cid varchar(255) BINARY NOT NULL default '', + data longblob, + expire int NOT NULL default '0', + created int NOT NULL default '0', + headers text, + PRIMARY KEY (cid), + INDEX expire (expire) + ) /*!40100 DEFAULT CHARACTER SET UTF8 */;"); break; } } @@ -48,6 +57,7 @@ function project_usage_uninstall() { 'project_usage_day', 'project_usage_week_project', 'project_usage_week_release', + 'cache_project_usage', ); foreach ($tables as $table) { if (db_table_exists($table)) { @@ -61,8 +71,32 @@ function project_usage_uninstall() { 'project_usage_life_daily', 'project_usage_life_weekly_project', 'project_usage_life_weekly_release', + 'project_usage_date_long', + 'project_usage_date_short', ); foreach ($variables as $variable) { variable_del($variable); } } + +/** + * Add a cache table {cache_project_release}. + */ +function project_usage_update_5000() { + $ret = array(); + switch ($GLOBALS['db_type']) { + case 'mysql': + case 'mysqli': + $ret[] = update_sql("CREATE TABLE IF NOT EXISTS {cache_project_usage} ( + cid varchar(255) BINARY NOT NULL default '', + data longblob, + expire int NOT NULL default '0', + created int NOT NULL default '0', + headers text, + PRIMARY KEY (cid), + INDEX expire (expire) + ) /*!40100 DEFAULT CHARACTER SET UTF8 */;"); + break; + } + return $ret; +} Index: project_usage.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project/usage/project_usage.module,v retrieving revision 1.4 diff -u -p -u -r1.4 project_usage.module --- project_usage.module 14 Aug 2007 00:51:53 -0000 1.4 +++ project_usage.module 19 Aug 2007 20:34:44 -0000 @@ -29,6 +29,13 @@ define('PROJECT_USAGE_WEEK', PROJECT_USA // Number of seconds in a year. define('PROJECT_USAGE_YEAR', PROJECT_USAGE_DAY * 365); +// Date formats for month and day. We define our own rather than using core's +// 'date_format_short' and 'date_format_long' variables because our timestamps +// don't have hour or minute resolution so displaying that would be confusing +// and take up extra space. +define('PROJECT_USAGE_DATE_LONG', variable_get('project_usage_date_long', 'F jS')); +define('PROJECT_USAGE_DATE_SHORT', variable_get('project_usage_date_short', 'M j')); + /** * Implementation of hook_menu(). */ @@ -43,11 +50,233 @@ function project_usage_menu($may_cache) 'description' => t('Configure how long usage data is retained.'), 'weight' => 1, ); + $items[] = array( + 'path' => 'project-usage', + 'title' => t('Project usage'), + 'callback' => 'project_usage_overview', + 'access' => user_access('view project usage'), + ); + } + else { + // Project and release usage tabs. + if (arg(0) == 'node' && is_numeric(arg(1))) { + $node = node_load(arg(1)); + if ($node->nid && ($node->type == 'project_project' || $node->type == 'project_release')) { + $items[] = array( + 'path' => 'node/'. arg(1) .'/usage', + 'title' => t('Usage'), + 'access' => user_access('view project usage'), + 'callback' => ($node->type == 'project_project') ? 'project_usage_project_page' : 'project_usage_release_page', + 'callback arguments' => array($node), + 'type' => MENU_LOCAL_TASK, + 'weight' => 2, + ); + } + } } return $items; } /** + * Implementation of hook_help(). + */ +function project_usage_help($section) { + switch ($section) { + case 'project-usage': + return t('The following is a summary of the usage information for the projects on this site. The count is the total number of sites using any version of the project. Only sites that have opted to allow usage information to be tracked are included.'); + + case 'admin/modules#description': + return t('Collects and processes usage information about projects and releases.'); + } +} + +/** + * Implementation of hook_perm(). + */ +function project_usage_perm() { + $perms = array( + 'view project usage', + ); + return $perms; +} + +/** + * Display and overview of usage for all modules. + */ +function project_usage_overview() { + drupal_set_title(t('Project usage overview')); + + $week_count = 6; + $weeks = project_usage_get_weeks_since(time() - ($week_count * PROJECT_USAGE_WEEK)); + // array_pop of the current week, since we won't have usage data for that, + // then reverse the order so it's newest to oldest. + array_pop($weeks); + $weeks = array_reverse($weeks); + + // In order to get the project usage data into a sortable table, we need to + // build a query that returns all the projects in {node} table LEFT JOINed + // to the {project_usage_week_project} table for each week of usage that + // we're going to list. We have to use the LEFT JOIN, rather than the more + // efficient INNER JOIN, because some weeks may not have usage data which + // would cause the row to be omitted. + $header = array(array('field' => 'title', 'data' => t('Project'), 'sort' => 'asc')); + $fields = array('n.nid', 'n.title'); + $joins = array(); + foreach ($weeks as $i => $week) { + $header[] = array( + 'field' => "week{$i}", + 'data' => format_date($week, 'custom', PROJECT_USAGE_DATE_SHORT), + ); + $fields[] = "p{$i}.count AS week{$i}"; + $joins[] = "LEFT JOIN {project_usage_week_project} p{$i} ON n.nid = p{$i}.nid AND p{$i}.timestamp = $week"; + } + + + // Check for a cached page. The cache id needs to take into account the page + // and sort column and order. + $sort = tablesort_init($header); + $cid = 'overview:'. $sort['sql'] .':'. $sort['sort']; + if ($cached = cache_get($cid, 'cache_project_usage')) { + return $cached->data; + } + + + // Fire off all those LEFT JOINs... you can almost hear the DB crying. + $result = db_query('SELECT '. implode(', ', $fields) .' FROM {node} n ' + . implode(' ', $joins) .' WHERE n.nid IN (SELECT nid FROM {project_usage_week_project})' + . tablesort_sql($header)); + + // Take the rows and force any NULL counts to zero. + while ($line = db_fetch_array($result)) { + $row = array(l($line['title'], 'node/'. $line['nid'] .'/usage')); + for ($i = 0; $i < $week_count; $i++) { + $row[] = (int) $line["week{$i}"]; + } + $rows[] = $row; + } + $output = theme('table', $header, $rows); + + + // Cache the completed page. + cache_set($cid, 'cache_project_usage', $output); + return $output; +} + +/** + * Display the usage history of a project node. + */ +function project_usage_project_page($node) { + // Setup the bread crumbs. + project_project_set_breadcrumb($node, $breadcrumb); + drupal_set_title(check_plain($node->title)); + + + // Check for a cached page. + $cid = "project:{$node->nid}"; + if ($cached = cache_get($cid, 'cache_project_usage')) { + return $cached->data; + } + + + // Build a table of the weekly usage data with a column for each API version. + $output .= '