[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/project/usage/includes/ -> pages.inc (source)

   1  <?php
   2  // $Id: pages.inc,v 1.3 2009/08/10 21:02:08 dww Exp $
   3  
   4  /**
   5   * @file
   6   * Code necessary for the menu callbacks on pages that display usage data.
   7   */
   8  
   9  /**
  10   * Helper function to display project usage help.
  11   *
  12   * @param $key
  13   *   URL argument that either identifies a project or a release node (either a
  14   *   node ID or a project short name), or NULL for the site-wide usage page.
  15   */
  16  function _project_usage_help($key) {
  17    if (empty($key)) {
  18      return '<p>'. t('This page summarizes the usage of all projects on @site_name. For each week beginning on the given date the figures show the number of sites that reported they are using (any version of) the project. Detailed usage information for each release of a project is available by clicking the project name.', array('@site_name' => variable_get('site_name', t('this site')))) .'</p>';
  19    }
  20    elseif (is_numeric($key)) {
  21      $node = node_load($key);
  22    }
  23    else {
  24      $node = project_project_retrieve($key);
  25    }
  26    switch ($node->type) {
  27      case 'project_project':
  28        return '<p>'. t('This page provides information about the usage of the %project_title project, including summaries across all versions and details for each release. For each week beginning on the given date the figures show the number of sites that reported they are using a given version of the project. ', array('%project_title' => $node->title)) .'</p>'; 
  29  
  30      case 'project_release':
  31        return '<p>'. t('For each week beginning on a given date, the figures show the number of sites that reported they are using the %release_title release. ', array('%release_title' => $node->title)) .'</p>'; 
  32  
  33    }
  34  }
  35  
  36  /**
  37   * Menu handler for project URLs.
  38   *
  39   * @param $key
  40   *   Node id or project uri. project nids and uris get the project usage page,
  41   *   release nids get the release usage page, and everything else gets a not
  42   *   found. In addition, if a user does not have permission to view the project
  43   *   or release node they've requested, they get an access denied page.
  44   */
  45  function project_usage_dispatch($key) {
  46    // Load the node the user has requested.  We want to only use
  47    // project_project_retrieve() if the $key parameter is not numeric because
  48    // project_project_retrieve() will only return a project_project node, and
  49    // we want to allow $node to also be a project_release node.
  50    if (is_numeric($key)) {
  51      $node = node_load($key);
  52    }
  53    else {
  54      $node = project_project_retrieve($key);
  55    }
  56  
  57    if (!empty($node->nid)) {
  58      // Make sure that the user has the permission to view this project/
  59      // project_release node.
  60      if (node_access('view', $node)) {
  61        if ($node->type == 'project_project') {
  62          return project_usage_project_page($node);
  63        }
  64        if ($node->type == 'project_release') {
  65          return project_usage_release_page($node);
  66        }
  67      }
  68      else {
  69        return drupal_access_denied();
  70      }
  71    }
  72    return drupal_not_found();
  73  }
  74  
  75  /**
  76   * Display an overview of usage for all modules.
  77   */
  78  function project_usage_overview() {
  79    drupal_add_css(drupal_get_path('module', 'project_usage') .'/project_usage.css');
  80    // Load the API functions we need for manipulating dates and timestamps.
  81    module_load_include('inc', 'project_usage', 'includes/date_api');
  82  
  83    // Grab an array of active week timestamps.
  84    $weeks = project_usage_get_active_weeks();
  85  
  86    // In order to get the project usage data into a sortable table, we've gotta
  87    // write a pretty evil query:
  88    //
  89    // - We need to create a separate column for each week to allow sorting by
  90    //   usage in any week (the tablesort_sql() requires that anything you can
  91    //   sort on has a distinct field in the underlying query). However, some
  92    //   weeks may not have any usage data, forcing us to use a LEFT JOIN,
  93    //   rather than the more efficient INNER JOIN.
  94    // - The LEFT JOINs mean we have to limit the entries in {node} so that
  95    //   we're not including things like forum posts, hence the WHERE IN below.
  96    // - Each project may have multiple records in {project_usage_week_project}
  97    //   to track usage for API version. We need to SUM() them to get a total
  98    //   count forcing us to GROUP BY. Sadly, I can't explain why we need
  99    //   SUM(DISTINCT)... it just works(TM).
 100    $sql_elements = project_usage_empty_query();
 101    // Ignore the order_bys generated by project_usage_empty_query(), and use
 102    // the tablesort instead.
 103    unset($sql_elements['order_bys']);
 104  
 105    $where_args = array();
 106    $sql_elements['fields']['pieces'] = array('n.nid', 'n.title', 'pp.uri');
 107    $sql_elements['from']['pieces'][] = '{node} n ';
 108    $sql_elements['joins']['pieces'][] = "INNER JOIN {project_projects} pp ON n.nid = pp.nid";
 109    $sql_elements['wheres']['pieces'] = array('n.nid IN (SELECT nid FROM {project_usage_week_project}) AND n.status = %d');
 110    $where_args[] = 1;  // n.status = 1
 111    $sql_elements['group_bys']['pieces'] = array('n.nid', 'n.title');
 112    $headers = array(
 113      t('#'),
 114      array('field' => 'n.title', 'data' => t('Project')),
 115    );
 116  
 117    $joins_args = array();
 118    foreach ($weeks as $i => $week) {
 119      // Note that "{$i}" in these query snippets are used to add a week integer
 120      // to the table and field aliases so we can uniquely identify each column
 121      // for sorting purposes.  These are not literals in the query, we need
 122      // these aliases to be unique via this PHP string operation before we even
 123      // build the query.
 124      $sql_elements['fields']['pieces'][] = "SUM(DISTINCT p{$i}.count) AS week{$i}";
 125      $sql_elements['joins']['pieces'][] = "LEFT JOIN {project_usage_week_project} p{$i} ON n.nid = p{$i}.nid AND p{$i}.timestamp = %d";
 126      $joins_args[] = $week;
 127  
 128      $header = array(
 129        'field' => "week{$i}",
 130        'data' => format_date($week, 'custom', variable_get('project_usage_date_short', PROJECT_USAGE_DATE_SHORT), 0),
 131        'class' => 'project-usage-numbers'
 132      );
 133      if ($i == 0) {
 134        $header['sort'] = 'desc';
 135      }
 136      $headers[] = $header;
 137    }
 138  
 139    // Check for a cached page. The cache id needs to take into account the sort
 140    // column and order.
 141    $sort = tablesort_init($headers);
 142    $cid = 'overview:'. $sort['sql'] .':'. $sort['sort'];
 143    if (project_can_cache() && $cached = cache_get($cid, 'cache_project_usage')) {
 144      return $cached->data;
 145    }
 146  
 147    $args = array_merge($joins_args, $where_args);
 148    $result = db_query(project_usage_build_query($sql_elements) . tablesort_sql($headers), $args);
 149  
 150    $number = 1;
 151    while ($line = db_fetch_array($result)) {
 152      $row = array(
 153        array('data' => $number++),
 154        array('data' => l($line['title'], 'project/usage/'. $line['uri'])),
 155      );
 156      foreach ($weeks as $i => $week) {
 157        $row[] = array('data' => number_format($line["week{$i}"]), 'class' => 'project-usage-numbers');
 158      }
 159      $rows[] = $row;
 160    }
 161    $output = theme('table', $headers, $rows, array('id' => 'project-usage-all-projects'));
 162  
 163    // Cache the completed page.
 164    if (project_can_cache()) {
 165      cache_set($cid, $output, 'cache_project_usage', project_usage_cache_time());
 166    }
 167  
 168    return $output;
 169  }
 170  
 171  /**
 172   * Display the usage history of a project node.
 173   */
 174  function project_usage_project_page($node) {
 175    drupal_add_css(drupal_get_path('module', 'project_usage') .'/project_usage.css');
 176    // Load the API functions we need for manipulating dates and timestamps.
 177    module_load_include('inc', 'project_usage', 'includes/date_api');
 178  
 179    $breadcrumb = array(
 180      l(t('Usage'), 'project/usage'),
 181    );
 182    project_project_set_breadcrumb(NULL, $breadcrumb);
 183    drupal_set_title(t('Usage statistics for %project', array('%project' => $node->title)));
 184  
 185    // In order to keep the database load down we need to cache these pages.
 186    // Because the release usage table is sortable, the cache id needs to take
 187    // into account the sort parameters. The easiest way to ensure we have valid
 188    // sorting parameters is to build the table headers and let the tablesort
 189    // functions do it. This means we end up doing most of the work to build the
 190    // page's second table early on. We might as well finish the job, then build
 191    // the other table and output them in the correct order.
 192  
 193    // Grab an array of active week timestamps.
 194    $weeks = project_usage_get_active_weeks();
 195  
 196    $releases = project_release_get_releases($node, FALSE);
 197  
 198    // If there are no releases for this project, we can skip the rest
 199    // of this function.
 200    if (empty($releases)) {
 201      return theme('project_usage_project_page', $node);
 202    }
 203  
 204    // Build a table showing this week's usage for each release. In order to get
 205    // the release usage data into a sortable table, we need another evil query:
 206    // - We need to create a separate column for each week to allow sorting by
 207    //   usage in any week (the tablesort_sql() requires that anything you can
 208    //   sort on has a distinct field in the underlying query). However, some
 209    //   weeks may not have any usage data, forcing us to use a LEFT JOIN,
 210    //   rather than the more efficient INNER JOIN.
 211    // - We need to create a column for each week but some weeks may not have any
 212    //   usage data, forcing us to use a LEFT JOIN, rather than the more
 213    //   efficient INNER JOIN.
 214    // - The LEFT JOINs mean we have to limit the entries in {node} so that we're
 215    //   not including things like forum posts, hence the WHERE IN below.
 216    $sql_elements = project_usage_empty_query();
 217    // Ignore the order_bys generated by project_usage_empty_query(), and use
 218    // the tablesort instead.
 219    unset($sql_elements['order_bys']);
 220  
 221    $sql_elements['fields']['pieces'] = array('n.nid');
 222    $sql_elements['from']['pieces'][] = '{node} n ';
 223    $sql_elements['wheres']['pieces'] = array('n.nid IN ('. implode(', ', array_fill(0, count($releases), '%d')) .') AND n.status = %d');
 224    $where_args = array_keys($releases);
 225    $where_args[] = 1;  // n.status = 1
 226  
 227    $release_header = array(array('field' => 'n.title', 'data' => t('Release'), 'sort' => 'desc'));
 228  
 229    $joins_args = array();
 230    foreach ($weeks as $i => $week) {
 231      // Note that "{$i}" in these query snippets are used to add a week integer
 232      // to the table and field aliases so we can uniquely identify each column
 233      // for sorting purposes.  These are not literals in the query, we need
 234      // these aliases to be unique via this PHP string operation before we even
 235      // build the query.
 236      $sql_elements['fields']['pieces'][] = "p{$i}.count AS week{$i}";
 237      $sql_elements['joins']['pieces'][] = "LEFT JOIN {project_usage_week_release} p{$i} ON n.nid = p{$i}.nid AND p{$i}.timestamp = %d";
 238      $joins_args[] = $week;
 239  
 240      $release_header[] = array(
 241        'field' => "week{$i}",
 242        'data' => format_date($week, 'custom', variable_get('project_usage_date_short', PROJECT_USAGE_DATE_SHORT), 0),
 243        'class' => 'project-usage-numbers',
 244      );
 245    }
 246  
 247    // Check for a cached page. The cache id needs to take into account the sort
 248    // column and order.
 249    $sort = tablesort_init($release_header);
 250    $cid = 'project:'. $node->nid .':'. $sort['sql'] .':'. $sort['sort'];
 251    if ($cached = cache_get($cid, 'cache_project_usage')) {
 252      return $cached->data;
 253    }
 254  
 255    $args = array_merge($joins_args, $where_args);
 256    $result = db_query(project_usage_build_query($sql_elements) . tablesort_sql($release_header), $args);
 257  
 258    $release_rows = array();
 259    while ($line = db_fetch_array($result)) {
 260      $sum = 0;
 261      $row = array(array('data' => l($releases[$line['nid']], 'project/usage/'. $line['nid'])));
 262      foreach ($weeks as $i => $week) {
 263        $sum += $line["week{$i}"];
 264        $row[] = array('data' => number_format($line["week{$i}"]), 'class' => 'project-usage-numbers');
 265      }
 266      // Skip any release with no usage.
 267      if ($sum) {
 268        $release_rows[] = $row;
 269      }
 270    }
 271  
 272    // Build a table of the weekly usage data with a column for each API version.
 273    // Get an array of the weeks going back as far as we have data...
 274    $oldest = db_result(db_query("SELECT MIN(puwp.timestamp) FROM {project_usage_week_project} puwp WHERE puwp.nid = %d", $node->nid));
 275    if ($oldest === NULL) {
 276      $weeks = array();
 277    }
 278    else {
 279      $weeks = project_usage_get_weeks_since($oldest);
 280      // ...ignore the current week, since we won't have usage data for that and
 281      // reverse the order so it's newest to oldest.
 282      array_pop($weeks);
 283      $weeks = array_reverse($weeks);
 284    }
 285  
 286    // The number of columns varies depending on how many different API versions
 287    // are in use. Set up the header and a blank, template row, based on the
 288    // number of distinct terms in use. This *could* be done with LEFT JOINs,
 289    // but it ends up being a more expensive query and harder to read.
 290    $project_header = array(0 => array('data' => t('Week')));
 291    $blank_row = array(0 => array('data' => ''));
 292    $result = db_query("SELECT DISTINCT td.tid, td.name FROM {project_usage_week_project} p INNER JOIN {term_data} td ON p.tid = td.tid WHERE p.nid = %d ORDER BY td.weight DESC, td.name", $node->nid);
 293    while ($row = db_fetch_object($result)) {
 294      $project_header[$row->tid] = array('data' => check_plain($row->name), 'class' => 'project-usage-numbers');
 295      $blank_row[$row->tid] = array('data' => 0, 'class' => 'project-usage-numbers');
 296    }
 297    $project_header['total'] = array('data' => t('Total'), 'class' => 'project-usage-numbers');
 298    $blank_row['total'] = array('data' => 0, 'class' => 'project-usage-numbers');
 299  
 300    // Now create a blank table with a row for each week and formatted date in
 301    // the first column...
 302    $project_rows = array();
 303    foreach ($weeks as $week) {
 304      $project_rows[$week] = $blank_row;
 305      $project_rows[$week][0]['data'] = format_date($week, 'custom', variable_get('project_usage_date_long', PROJECT_USAGE_DATE_LONG), 0);
 306    }
 307  
 308    // ...then fill it in with our data.
 309    $result = db_query("SELECT timestamp, tid, count FROM {project_usage_week_project} WHERE nid = %d", $node->nid);
 310    while ($row = db_fetch_object($result)) {
 311      $project_rows[$row->timestamp][$row->tid]['data'] = $row->count;
 312    }
 313  
 314    // ...and add the weekly totals across all API versions
 315    foreach ($project_rows as $timestamp => $row) {
 316      $total = 0;
 317      foreach ($row as $tid => $data) {
 318        if (!empty($tid) && !empty($data['data'])) {
 319          $total += $data['data'];
 320          $project_rows[$timestamp][$tid]['data'] = number_format($data['data']);
 321        }
 322      }
 323      $project_rows[$timestamp]['total']['data'] = number_format($total);
 324    }
 325  
 326    $output = theme('project_usage_project_page', $node, $release_header, $release_rows, $project_header, $project_rows);
 327  
 328    // Cache the completed page.
 329    if (project_can_cache()) {
 330      cache_set($cid, $output, 'cache_project_usage', project_usage_cache_time());
 331    }
 332  
 333    return $output;
 334  }
 335  
 336  /**
 337   * Display the usage history of a release node.
 338   */
 339  function project_usage_release_page($node) {
 340    drupal_add_css(drupal_get_path('module', 'project_usage') .'/project_usage.css');
 341  
 342    // Load the API functions we need for manipulating dates and timestamps.
 343    module_load_include('inc', 'project_usage', 'includes/date_api');
 344  
 345    $project = node_load($node->project_release['pid']);
 346    $breadcrumb = array(
 347      l(t('Usage'), 'project/usage'),
 348      l($project->title, 'project/usage/'. $project->nid),
 349    );
 350    project_project_set_breadcrumb(NULL, $breadcrumb);
 351    drupal_set_title(t('Usage statistics for %release', array('%release' => $node->title)));
 352  
 353    // Check for a cached page.
 354    $cid = "release:{$node->nid}";
 355    if (project_can_cache() && $cached = cache_get($cid, 'cache_project_usage')) {
 356      return $cached->data;
 357    }
 358  
 359    // Table displaying the usage back through time.
 360    $header = array(
 361      array('data' => t('Week starting')),
 362      array('data' => t('Count'), 'class' => 'project-usage-numbers'),
 363    );
 364    $rows = array();
 365    $query = db_query("SELECT timestamp, count FROM {project_usage_week_release} WHERE nid = %d ORDER BY timestamp DESC", $node->nid);
 366    while ($row = db_fetch_object($query)) {
 367      $rows[] = array(
 368        array('data' => format_date($row->timestamp, 'custom', variable_get('project_usage_date_long', PROJECT_USAGE_DATE_LONG), 0)),
 369        array('data' => number_format($row->count), 'class' => 'project-usage-numbers'),
 370      );
 371    }
 372    $output = theme('project_usage_release_page', $project, $node, $header, $rows);
 373  
 374    // Cache the completed page.
 375    if (project_can_cache()) {
 376      cache_set($cid, $output, 'cache_project_usage', project_usage_cache_time());
 377    }
 378  
 379    return $output;
 380  }
 381  
 382  /**
 383   * Theme the output of project/usage/<project> page.
 384   *
 385   * @param $project
 386   *   A fully loaded $node object for a project.
 387   * @param $release_header
 388   *   A table header for the release usage table.
 389   * @param $release_rows
 390   *   Table rows for the release usage table.
 391   * @param $project_header
 392   *   A table header for the weekly project usage table.
 393   * @param $project_rows
 394   *   Table rows for the weekly project usage table.
 395   */
 396  function theme_project_usage_project_page($project, $release_header = NULL, $release_rows = NULL, $project_header = NULL, $project_rows = NULL) {
 397    $output = theme('project_usage_header_links', $project);
 398  
 399    if (empty($release_rows)) {
 400      // There are no published releases for a project that the user has access
 401      // to view.
 402      $output .= '<p>'. t('There is no usage information for any release of this project.') .'</p>';
 403      return $output;
 404    }
 405  
 406    $output .= '<h3>'. t('Weekly project usage') .'</h3>';
 407    $output .= theme('project_usage_chart_by_release', t('Weekly @project usage by API version', array('@project' => $project->title)), $project_header, $project_rows);
 408    $output .= theme('table', $project_header, $project_rows, array('id' => 'project-usage-project-api'));
 409    $output .= '<h3>'. t('Recent release usage') .'</h3>';
 410    $output .= theme('table', $release_header, $release_rows, array('id' => 'project-usage-project-releases'));
 411    return $output;
 412  }
 413  
 414  /**
 415   * Theme the output of the project/usage/<release nid> page.
 416   *
 417   * @param $project
 418   *   A fully loaded $node object for a project.
 419   * @param $release
 420   *   A fully loaded $node object for a release.
 421   * @param $header
 422   *   A table header for the release usage table.
 423   * @param $rows
 424   *   Table rows for the release usage table.
 425   */
 426  function theme_project_usage_release_page($project, $release, $header, $rows) {
 427    $output = theme('project_usage_header_links', $project, $release);
 428  
 429    // If there is no usage information for a release, don't just
 430    // display an empty usage table.
 431    if (empty($rows)) {
 432      $output .= '<p>' . t('There is no usage information for this release.') . '</p>';
 433      return $output;
 434    }
 435  
 436    $output .= theme('project_usage_chart_by_release', t('Weekly @release usage', array('@release' => $release->title)), $header, $rows);
 437    $output .= theme('table', $header, $rows, array('id' => 'project-usage-release'));
 438    return $output;
 439  }
 440  
 441  /**
 442   * Create list of links at the top of a usage statistics page.
 443   *
 444   * @param $project
 445   *   A fully loaded $node object for a project.
 446   * @param $release
 447   *   If the current statistics page is for a release, the fully loaded $node
 448   *   object for that release.
 449   *
 450   * @return
 451   *   Themed HTML of a list of links for the top of a statistics page.
 452   */
 453  function theme_project_usage_header_links($project, $release = NULL) {
 454    $links = array();
 455    $links[] = l(t('%project_name project page', array('%project_name' => $project->title)), 'node/'. $project->nid, array('html' => TRUE));
 456    if (!empty($release)) {
 457      $links[] = l(t('%release_name release page', array('%release_name' => $release->title)), 'node/' . $release->nid, array('html' => TRUE));
 458      $links[] = l(t('All %project_name usage statistics', array('%project_name' => $project->title)), 'project/usage/' . $project->project['uri'], array('html' => TRUE));
 459    }
 460    $links[] = l(t('Usage statistics for all projects'), 'project/usage');
 461    return theme('item_list', $links);
 462  }
 463  
 464  /**
 465   * Convert the usage table data into a Google Chart image.
 466   *
 467   * First column should be the weeks, each subsequent column should be a data
 468   * series.
 469   *
 470   * @param $header
 471   *   A table header for the weekly usage table.
 472   * @param $rows
 473   *   Table rows for the weekly usage table.
 474   * @return
 475   *   An HTML IMG element, or empty string if there is an error such as
 476   *   insufficent data.
 477   */
 478  function theme_project_usage_chart_by_release($title, $header, $rows) {
 479    // Make sure we have enough data to make a useful chart.
 480    if (count($rows) < 2 || count($header) < 2) {
 481      return '';
 482    }
 483  
 484    // Reverse the order of the rows so it's oldest to newest.
 485    $rows = array_reverse($rows);
 486  
 487    // Pull the API versions from the table header for use as a legend. Since the
 488    // table is keyed strangely, make note of which tid is in which order so we
 489    // can efficiently iterate over the columns.
 490    $legend = array();
 491    $mapping = array();
 492    foreach ($header as $tid => $cell) {
 493      $legend[] = $cell['data'];
 494      $mapping[] = $tid;
 495    }
 496    // Drop the date column from the legend and mapping since it's the other axis.
 497    unset($legend[0]);
 498    unset($mapping[0]);
 499  
 500    // Rotate the table so each series is in a row in the array and grab the
 501    // dates for use as axis labels.
 502    $series = array();
 503    $date_axis = array();
 504    foreach (array_values($rows) as $i => $row) {
 505      $date_axis[$i] = $row[0]['data'];
 506      foreach ($mapping as $j => $tid) {
 507        // FIXME: The table values have commas in them from number_format(). We
 508        // need to remove them because we'll use commas to separate the values
 509        // in the URL string. It might be better to pass in clean number values
 510        // and format them here rather than have to uncook them.
 511        $series[$j][$i] = (int) str_replace(',', '', $row[$tid]['data']);
 512      }
 513    }
 514  
 515    // Now convert the series into strings with the data. Along the way figure
 516    // out the range of data.
 517    $min = $max = 0;
 518    $data = array();
 519    foreach ($series as $s) {
 520      $data[] = implode(',', $s);
 521      $max = max($max, max($s));
 522    }
 523  
 524    // Round the max up to the next decimal place (3->10, 19->20, 8703->9000) so
 525    // that the labels have round numbers and the entire range is visible. We're
 526    // forced to build the number as a string because PHP's round() function
 527    // can round down (3->0, not 3->10) and it returns floats that loose
 528    // precision (causing 90,000 to display as 89,999.99). We pull off the first
 529    // digit, add 1 to that, and then pad the rest with zeros.
 530    $zeros = strlen($max) - 1;
 531    $max = ($zeros > 0) ? (substr($max, 0, 1) + 1 . str_repeat('0', $zeros)) : 10;
 532    $value_axis = range($min, $max, '1'. str_repeat('0', $zeros));
 533  
 534    // Might need more colors than this.
 535    $colors = array('EDAA00', '0062A0', 'A17300', 'ED8200', '38B4BA', '215D6E');
 536  
 537    // The key values in this array are dictated by the Google Charts API:
 538    // http://code.google.com/apis/chart/
 539    $args = array(
 540      // Chart title.
 541      'chtt' => check_plain($title),
 542      // Dimensions as width x height in pixels.
 543      'chs' => '600x200',
 544      // Chart type
 545      'cht' => 'lc',
 546      // Pick some colors.
 547      'chco' => implode(',', array_slice($colors, 0, count($series))),
 548      'chd' =>  't:'. implode('|', $data),
 549      // Set the range of the chart
 550      'chds' => implode(',', array_fill(0, count($series), $min .','. $max)),
 551       // Legend is the header titles after excluding the date.
 552      'chdl' => implode('|', $legend),
 553    );
 554    project_usage_chart_axis_labels($args, array(
 555      'x' => project_usage_chart_label_subset($date_axis, 5),
 556      'y' => array_map('number_format', project_usage_chart_label_subset($value_axis, 5)),
 557    ));
 558  
 559    return theme('project_usage_chart', $args);
 560  }
 561  
 562  /**
 563   * Extract a subset of labels at regular intervals from a set.
 564   *
 565   * @param $labels
 566   *   Array of values to choose from.
 567   * @param $n
 568   *   Number of segments in the set. This number should divide $labels with no
 569   *   remander.
 570   */
 571  function project_usage_chart_label_subset($labels, $n) {
 572    $subset = array();
 573    $count = count($labels) - 1;
 574    // We can't give them back more labels that we're given.
 575    $n = min($count, $n - 1);
 576    for ($i = 0; $i <= $n; $i++) {
 577      $subset[] = $labels[(int) ($count * ($i / $n))];
 578    }
 579    return $subset;
 580  }
 581  
 582  /**
 583   * Add axis labels to the chart arguments.
 584   *
 585   * @param $args
 586   *   The array where the chart is being built.
 587   * @param $labels
 588   *   Array keyed by axis (x, t, y, r) the value is an array of labels for that
 589   *   axis.
 590   * @see http://code.google.com/apis/chart/labels.html#multiple_axes_labels
 591   */
 592  function project_usage_chart_axis_labels(&$args, $labels) {
 593    $keys = array_keys($labels);
 594    $args['chxt'] = implode(',', $keys);
 595  
 596    $l = array();
 597    foreach ($keys as $i => $key) {
 598      $l[$i] = $i .':|' . implode('|', $labels[$key]);
 599    }
 600    $args['chxl'] = implode('|', $l);
 601  }
 602  
 603  /**
 604   * Convert the array of Google Chart paramters into an image URL.
 605   *
 606   * @param $args
 607   *   Array of key, value pairs to turn into a Google Charts image.
 608   * @return
 609   *   HTML image element.
 610   */
 611  function theme_project_usage_chart($args) {
 612    $params = array();
 613    foreach ($args as $key => $value) {
 614      $params[] = $key .'='. $value;
 615    }
 616    // If the chart has a title use that for the image's alt and title values.
 617    $title = empty($args['chtt']) ? '' : $args['chtt'];
 618    return theme('image', 'http://chart.apis.google.com/chart?'. implode('&', $params), $title, $title, NULL, FALSE);
 619  }
 620  
 621  /**
 622   * Begin crap old project code.
 623   *
 624   * This is old project module code that project_usage is still leveraging.
 625   * At some point we need to rip this out and do something better, perhaps
 626   * when core gets its own query builder?
 627   */
 628  
 629  /**
 630   * Construct an empty query for project_usage_build_query().
 631   *
 632   * Set the default elements that will be used to construct the SQL statement.
 633   */
 634  function project_usage_empty_query() {
 635    return array(
 636      'fields' => array(
 637        'prefix' => 'SELECT ',
 638        'glue' => ', ',
 639        'pieces' => array(),
 640      ),
 641      'from' => array(
 642        'prefix' => ' FROM ',
 643        'glue' => NULL,
 644        'pieces' => array(''),
 645      ),
 646      'joins' => array(
 647        'prefix' => '',
 648        'glue' => ' ',
 649        'pieces' => array(),
 650      ),
 651      'wheres' => array(
 652        'prefix' => ' WHERE ',
 653        'glue' => ' AND ',
 654        'pieces' => array(),
 655      ),
 656      'group_bys' => array(
 657        'prefix' => ' GROUP BY ',
 658        'glue' => ', ',
 659        'pieces' => array(),
 660      ),
 661      'order_bys' => array(
 662        'prefix' => ' ORDER BY ',
 663        'glue' => ', ',
 664        'pieces' => array(),
 665      ),
 666      'parameters' => array(
 667        'prefix' => NULL,
 668        'glue' => NULL,
 669        'pieces' => array(),
 670      )
 671    );
 672  }
 673  
 674  /**
 675   * Build a SQL statment from a structured array.
 676   *
 677   * @param $sql_elements
 678   *   An array with the following keys:
 679   *     'fields', 'from', 'joins', 'wheres', 'group_bys', 'order_bys',
 680   *     'parameters'
 681   *   Each value is an array with the following keys:
 682   *     'prefix', 'glue', 'pieces'
 683   * @return
 684   *   SQL string.
 685   */
 686  function project_usage_build_query($sql_elements) {
 687    $sql = '';
 688    foreach ($sql_elements as $key => $sql_element) {
 689      if ($key != 'parameters' && count($sql_element['pieces'])) {
 690        $sql .= $sql_element['prefix'] . implode($sql_element['glue'], $sql_element['pieces']);
 691      }
 692    }
 693    return db_rewrite_sql($sql);
 694  }
 695  
 696  /**
 697   * End of old project crap ccode.
 698   */


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7