Index: project_issue/project_issue.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue/project_issue.module,v
retrieving revision 1.73
diff -u -p -r1.73 project_issue.module
--- project_issue/project_issue.module	13 Nov 2007 01:28:13 -0000	1.73
+++ project_issue/project_issue.module	23 Nov 2007 19:33:24 -0000
@@ -435,7 +435,11 @@ function project_issue_menu($may_cache) 
       }
     }
 
-    drupal_add_css(drupal_get_path('module', 'project_issue') .'/project_issue.css');
+    $project_issue_path = drupal_get_path('module', 'project_issue');
+    if (module_exists('views')) {
+      require './'. $project_issue_path .'/project_issue_views.inc';
+    }
+    drupal_add_css($project_issue_path .'/project_issue.css');
   }
   return $items;
 }
Index: project_issue/project_issue_views.inc
===================================================================
RCS file: project_issue/project_issue_views.inc
diff -N project_issue/project_issue_views.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ project_issue/project_issue_views.inc	23 Nov 2007 19:33:24 -0000
@@ -0,0 +1,677 @@
+<?php
+
+/**
+ * @file
+ * Provides support for Views integration.
+ *
+ * Currently, this only exposes project_issue-related data, but does
+ * not define any default views.
+ */
+
+/**
+ * Implementation of hook_views_tables
+ */
+function project_issue_views_tables() {
+  // It is not strictly necessary to include $tables['node'] below,
+  // however if we use the default Node: Changed time field provided
+  // by the Views module the changed time is printed as "<time> ago".
+  // Since the 'ago' takes up extra space, we provide our own field
+  // here that uses a custom handler so that we can omit 'ago'.
+  $tables['node'] = array(
+    'name' => 'node',
+    'fields' => array(
+      'changed' => array(
+      'name' => t('Project issue: Updated Time'),
+      'sortable' => true,
+      'handler' => views_handler_field_project_issue_since,
+      'option' => 'string',
+      'help' => t('Display the last time the issue node was updated.'),
+      ),
+    ),
+  );
+
+  $tables['project_issues'] = array(
+    'name' => 'project_issues',
+    'join' => array(
+      'type' => 'inner',
+      'left' => array(
+        'table' => 'node',
+        'field' => 'nid'
+      ),
+      'right' => array(
+        'field' => 'nid'
+      ),
+    ),
+    'fields' => array(
+      // To be useful, 'pid' requires another JOIN on {node}, so see
+      // the project_issue_project_node definition below.
+      'category' => array(
+        'name' => t('Project Issue: Category'),
+        'sortable' => true,
+        'help' => t('The issue\'s category (bug, task, feature, etc).'),
+      ),
+      'component' => array(
+        'name' => t('Project Issue: Component'),
+        'sortable' => true,
+        'help' => t('The issue\'s component (the options are controlled per-project).'),
+      ),
+      'priority' => array(
+        'name' => t('Project Issue: Priority'),
+        'handler' => 'views_handler_field_project_issue_priority',
+        'sortable' => true,
+        'help' => t('The issue\'s priority (critical, normal, minor).'),
+      ),
+      'rid' => array(
+        'name' => t('Project Issue: Version'),
+        'query_handler' => 'views_query_handler_field_project_issue_version',
+        'handler' => 'views_handler_field_project_issue_version',
+        'sortable' => true,
+        'help' => t('The version associated with the issue (depends on project_release.module)'),
+      ),
+      'sid' => array(
+        'name' => t('Project Issue: Status'),
+        'help' => t('The status of each issue.'),
+        'sortable' => true,
+        'handler' => 'views_handler_field_project_issue_status',
+      ),
+     ),
+    'filters' => array(
+      'category' => array(
+        'name' => t('Project Issue: Category'),
+        'operator' => array('=' => t('Equals')),
+        'list' => 'views_handler_filter_project_issue_category',
+        'list-type' => 'select',
+        'help' => t('Filter by issue category'),
+      ),
+      'priority' => array(
+        'name' => t('Project Issue: Priority'),
+        'operator' => array('=' => t('Equals')),
+        'list' => 'views_handler_filter_project_issue_priority',
+        'list-type' => 'select',
+        'help' => t('Filter by issue priority'),
+      ),
+      'sid' => array(
+        'name' => t('Project Issue: Status'),
+        'operator' => array('=' => t('Equals')),
+        'list' => 'views_handler_filter_project_issue_status',
+        'list-type' => 'select',
+        'help' => t('Filter by issue status'),
+      ),
+    ),
+  );
+
+  // Add data from the project module
+  $tables['project_issue_project_node'] = array(
+    'name' => 'node',
+    'join' => array(
+      'type' => 'inner',
+      'left' => array(
+        'table' => 'project_issues',
+        'field' => 'pid'
+      ),
+      'right' => array(
+        'field' => 'nid'
+      ),
+    ),
+    'fields' => array(
+      'title' => array(
+        'name' => t('Project Issue Project Node: Title'),
+        'handler' => array(
+          'views_handler_field_project_issue_project_nodelink'           => t('Normal'),
+          'views_handler_field_project_issue_project_nodelink_with_mark' => t('With updated mark')
+        ),
+        'option' => array(
+           '#type' => 'select',
+           '#options' => array(
+             'link' => t('As link'),
+             'nolink' => t('Without link')
+            ),
+        ),
+        'sortable' => true,
+        'pid' => 'nid',
+        'addlfields' => array('changed', 'nid'),
+//        'query_handler' => 'views_query_handler_field_project_issue_project_node',
+        'help' => t('Display the title of the project.'),
+      ),
+    ),
+    'filters' => array(
+      'nid' => array(
+        'name' => t('Project Issue Project Node: Project'),
+        'operator' => array('=' => t('Equals')),
+        'list' => 'views_handler_filter_project_issue_project',
+        'list-type' => 'select',
+        'help' => t('Filter by project'),
+      ),
+    ),
+  );
+  // Add data from {project_projects}
+  $tables['project_issue_project_projects'] = array(
+    'name' => 'project_projects',
+    'join' => array(
+      'type' => 'inner',
+      'left' => array(
+        'table' => 'project_issues',
+        'field' => 'pid'
+      ),
+      'right' => array(
+        'field' => 'nid'
+      ),
+    ),
+    'fields' => array(
+      'uri' => array(
+        'name' => t('Project Issue Project Node: Short name'),
+        'sortable' => true,
+        'help' => t('Display the short name of the project.'),
+        'option' => array(
+           '#type' => 'select',
+           '#options' => array(
+             'project_issue_display' => t('Display this field'),
+             'project_issue_nodisplay' => t('Do not display this field')
+           ),
+        ),
+      ),
+    ),
+  );    
+  // Add data from the users module
+  $tables['users'] = array(
+    'name' => 'users',
+    'join' => array(
+      'type' => 'inner',
+      'left' => array(
+        'table' => 'project_issues',
+        'field' => 'assigned'
+      ),
+      'right' => array(
+        'field' => 'uid'
+      ),
+    ),
+    'fields' => array(
+    // TODO: handlers
+      'name' => array(
+        'name' => t('Project Issue: Assigned'),
+        'help' => t('The user a given issue is assigned to.'),
+        'sortable' => true,
+        'addlfields' => array('uid', 'name'),
+        'handler' => 'views_handler_field_project_issue_assigned',
+      ),
+    ),
+  );
+
+  return $tables;
+}
+
+/**
+ * Implementation of hook_views_arguments
+ */
+function project_issue_views_arguments() {
+  $arguments = array(
+    'project_issue_project' => array(
+      'name' => t('Project issue: Project node'),
+      'help' => t('This argument is either a single project short name or one or more project nids separated by + (OR) or , (AND).'),
+      'handler' => 'views_handler_argument_project_issue_project',
+      'option' => array(
+        '#type' => 'select',
+        '#options' => array('equal'),
+      ),
+    ),
+    'project_issue_status' => array(
+      'name' => t('Project issue: status'),
+      'help' => t('This argument is one or more project_issue status IDs.'),
+      'handler' => 'views_handler_argument_project_issue_status',
+      'option' => '',    
+     ),
+ );
+  return $arguments;
+}
+
+/**
+ *
+ */
+function views_handler_argument_project_issue_project($op, &$query, $argtype, $arg = '') {
+  switch($op) {
+    case 'summary':
+      $query->add_field("title");
+      $fieldinfo['field'] = 'project_issue_project_node.nid';
+      return $fieldinfo;
+    case 'sort':
+      // do nothing here.
+      break;
+    case 'filter':
+      $args = _views_break_phrase($arg);
+      if (!empty($args)) {
+        if ($args[0] == 'and') {
+          $operator = $argtype['options'] ? '!=' : '=';
+          foreach ($args[1] as $arg) {
+            $query->add_where("project_issue_project_node.nid $operator %d", $arg);
+          }
+        }
+        else {
+          $query->add_where("project_issue_project_node.nid IN (%s)", implode(',', $args[1]));
+        }
+      }
+      elseif (!empty($arg)) {
+        // Allow $arg to be a project short name instead of a nid.
+        $query->add_where("project_issue_project_projects.uri = '%s'", $arg);
+      }
+      break;
+    case 'link':
+      // @todo fix this
+      return l($query->title, "$arg/$query->nid");
+    case 'title':
+      // @todo get title to work right
+      $args = _views_break_phrase($query);
+      if (!empty($args)) {
+        if ($args[0] == 'and') {
+          $title = t('multiple projects');
+        }
+        else {
+          dpm(implode(',', $args[1]));
+          $titles = array();
+          $result = db_query("SELECT title FROM {node} n WHERE n.nid IN ('%s')", implode(',', $args[1]));
+// @todo this isn't quite working properly yet
+          while ($node = db_fetch_object($result)) {
+            $titles[] = t('%title', array('%title' => $node->title));
+          }
+          $title = implode(', ', $titles);
+        }
+      }
+      elseif (!empty($query)) {
+        // Allow $arg to be a project short name instead of a nid.
+        $result = db_query("SELECT title FROM {node} n INNER JOIN {project_projects} pp ON pp.nid = n.nid WHERE pp.uri = '%s'", $query);
+        while ($node = db_fetch_object($result)) {
+          $title = t('%project', array('%project' => $node->title));
+        }
+      }
+      if (empty($title)) {
+        $title = t('all projects');
+      }
+      return $title;
+      break;
+  }
+}
+
+/**
+ *
+ */
+function views_handler_argument_project_issue_status($op, &$query, $argtype, $arg = '') {
+
+}
+
+/**
+ * Displays a field indicating the status of an issue.
+ */
+function views_handler_field_project_issue_status($fieldinfo, $fielddata, $value, $data) {
+  return project_issue_state($value);
+}
+
+/**
+ * Displays a field indicating the priority of an issue.
+ */
+function views_handler_field_project_issue_priority($fieldinfo, $fielddata, $value, $data) {
+  return project_issue_priority($value);
+}
+
+/**
+ * Modifies the query for views that include a field for the issue's
+ * version, and JOIN's against {node} and {project_release_nodes} to
+ * find the version string of a given release id (rid).
+ *
+ * @todo Need to figure out how to make this really work.
+ */
+function views_query_handler_field_project_issue_version($fielddata, $fieldinfo, &$query) {
+/*
+  $joininfo = array(
+    'type' => 'LEFT',
+    'left' => array(
+      'table' => 'project_issues',
+      'field' => 'rid'
+    ),
+    'right' => array(
+      'field' => 'nid'
+    ),
+  );
+  $num = $query->add_table('node', false, 1, $joininfo);
+  $query->add_field('version', $query->get_table_name('node', $num), $field['tablename'] . '_name');
+*/
+}
+
+
+/**
+ * Add {project_projects} field 'uri' to the query results.
+ *
+ * @todo:  get this working as well
+ */
+/*
+function views_query_handler_field_project_issue_project_node($fielddata, $fieldinfo, &$query) {
+dpm($fielddata);
+dpm($fieldinfo);
+dpm($query);
+
+  $joininfo = array(
+    'type' => 'inner',
+    'left' => array(
+      'table' => $fielddata['tablename'],
+      'field' => 'nid'
+    ),
+    'right' => array(
+      'field' => 'nid'
+    ),
+  );
+  $num = $query->add_table('project_projects', false, 1, $joininfo);
+  $query->add_field('uri', $query->get_table_name('project_projects', $num), $fielddata['tablename'] . '_uri');
+dpm($query);
+}
+*/
+
+/**
+ * Format the assigned field as a username.
+ * Loosely based on views_handler_field_username, but doesn't return
+ * "Anonymous" when there is no one assigned.
+ */
+function views_handler_field_project_issue_assigned($fieldinfo, $fielddata, $value, $data) {
+  $name = '';
+  $obj = new stdClass();
+  $uidfield = $fielddata['tablename'] . "_"  . $fieldinfo['uid'];
+  if (is_numeric($data->users_uid) && $data->users_uid != 0) {
+    // Loading each user seems expensive here, considering we already ahve the data
+    // but we loose theme_username functionality
+    //$obj = user_load(array('uid' => $data->$uidfield));
+    //$name = theme('username', $obj);
+    $name = l($data->users_name, 'user/'.$data->users_uid);
+  }
+  else {
+    $name = '';
+  }
+  return $name;
+}
+
+//  dpm($fieldinfo);
+//  dpm($fielddata);
+//  dpm($value);
+//  dpm($data);
+function views_handler_field_project_issue_version($fieldinfo, $fielddata, $value, $data) {
+  if (!empty($data->project_issues_rid)) {
+    $release = node_load(array('nid' => $data->project_issues_rid));
+    if (!empty($release)) {
+      $version = project_release_get_version($release, $project = NULL);
+      if (!empty($version)) {
+        return $version;
+      }
+    }
+  }
+  
+}
+
+/*
+ * Format a project title to link to the project.
+ */
+function views_handler_field_project_issue_project_nodelink($fieldinfo, $fielddata, $value, $data) {
+  if ($fielddata['options'] == 'nolink') {
+    return check_plain($value);
+  }
+  return l($value, "node/$data->project_issue_project_node_nid");
+}
+
+/*
+ * Format a project title as a link to the project node with a 'mark' stating whether or not the project has
+ * updated since it was last viewed by the user.
+ */
+function views_handler_field_project_issue_project_nodelink_with_mark($fieldinfo, $fielddata, $value, $data) {
+  if ($fielddata['options'] == 'nolink') {
+    $link = check_plain($value);
+  }
+  else {
+    $link = l($value, "node/$data->project_issue_project_node_nid");
+  }
+  return $link .' '. theme('mark', node_mark($data->project_issue_project_node_nid, $data->project_issue_project_node_changed));
+}
+
+/**
+ * Format a date as "X time".
+ */
+function views_handler_field_project_issue_since($fieldinfo, $fielddata, $value, $data) {
+  return $data->node_changed ? t('!time', array('!time' => format_interval(time() - $data->node_changed, 2))) : theme('views_nodate');
+}
+
+/**
+ * Filter the list of projects by category
+ */
+function views_handler_filter_project_issue_category() {
+  return project_issue_category();
+}
+
+/**
+ * Filter the list of projects by priority
+ * HELP: Is project_issue_priority()supposed to be in the form of
+ * array(1 => 'critical', 'normal', 'minor');
+ */
+function views_handler_filter_project_issue_priority() {
+  //$priorities = project_issue_priority();
+  $priorities = array(1 => 'critical', 2 => 'normal', 3 => 'minor');
+  return $priorities;
+}
+
+
+/**
+ * Filter the list of status options to filter by status
+ */
+function views_handler_filter_project_issue_status() {
+  return project_issue_state();
+}
+
+/**
+ * Filter the list of projects to filter by project
+ */
+function views_handler_filter_project_issue_project() {
+  $project_urls = array();
+  $projects = project_projects_select_options($project_urls);
+  return $projects;
+}
+
+function theme_views_view_table_project_issues2($view, $nodes) {
+  $fields = _views_get_fields();
+  foreach ($nodes as $node) {
+    $class = "state-$node->project_issues_sid";
+    $row_data = array();
+    foreach ($view->field as $field) {
+      if ($fields[$field['id']]['visible'] !== FALSE) {
+        $cell['data'] = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node, $view);
+        $cell['class'] = "view-field ". views_css_safe('view-field-'. $field['queryname']);
+        $row_data[] = $cell;
+      }
+    }
+    $row_data = array('data' => $row_data, 'class' => $class);
+    $rows[] = $row_data;
+  }
+  $output = '<div class="project-issue">';
+  $output .= theme('table', $view->table_header, $rows);
+  $output .= '</div>';
+  return $output;
+}
+
+/**
+ * Implementation of hook_views_pre_view
+ */
+function project_issue_views_pre_view(&$view) {
+  // Remove any fields with option set to "project_issue_nodisplay"
+  // from the view so they aren't displayed.
+  foreach ($view->field as $index => $field) {
+    if ($field['options'] == 'project_issue_nodisplay') {
+      unset($view->field[$index]);
+      unset($view->table_header[$index]);
+    } 
+  }
+}
+
+/**
+ * Implementations of hook_views_default_views
+ */
+function project_issue_views_default_views() {
+  $view = new stdClass();
+  $view->name = 'project_issues2';
+  $view->description = '';
+  $view->access = array (
+);
+  $view->view_args_php = '';
+  $view->page = TRUE;
+  $view->page_title = 'Issues for all projects';
+  $view->page_header = '';
+  $view->page_header_format = '1';
+  $view->page_footer = '';
+  $view->page_footer_format = '1';
+  $view->page_empty = 'There are currently no issues to display for the criteria selected.';
+  $view->page_empty_format = '1';
+  $view->page_type = 'table';
+  $view->url = 'project_issues';
+  $view->use_pager = TRUE;
+  $view->nodes_per_page = '50';
+  $view->menu = TRUE;
+  $view->menu_title = 'Project Issues';
+  $view->menu_tab = FALSE;
+  $view->menu_tab_weight = '0';
+  $view->menu_tab_default = FALSE;
+  $view->menu_tab_default_parent = NULL;
+  $view->menu_tab_default_parent_type = 'tab';
+  $view->menu_parent_tab_weight = '0';
+  $view->menu_parent_title = '';
+  $view->sort = array (
+  );
+  $view->argument = array (
+    array (
+      'type' => 'project_issue_project',
+      'argdefault' => '2',
+      'title' => '',
+      'options' => '0',
+      'wildcard' => '',
+      'wildcard_substitution' => '',
+    ),
+  );
+  $view->field = array (
+    array (
+      'tablename' => 'project_issue_project_node',
+      'field' => 'title',
+      'label' => 'Project',
+      'handler' => 'views_handler_field_project_issue_project_nodelink_with_mark',
+      'sortable' => '1',
+      'options' => 'link',
+    ),
+    array (
+      'tablename' => 'node',
+      'field' => 'title',
+      'label' => 'Summary',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'sid',
+      'label' => 'Status',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'priority',
+      'label' => 'Priority',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'category',
+      'label' => 'Category',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'rid',
+      'label' => 'Version',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'node',
+      'field' => 'changed',
+      'label' => 'Last updated',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'users',
+      'field' => 'name',
+      'label' => 'Assigned to',
+      'sortable' => '1',
+    ),
+    array (
+      'tablename' => 'project_issue_project_projects',
+      'field' => 'uri',
+      'label' => 'Short name',
+      'options' => 'project_issue_nodisplay',
+    ),
+  );
+  $view->filter = array (
+    array (
+      'tablename' => 'project_issue_project_node',
+      'field' => 'nid',
+      'operator' => '=',
+      'options' => '',
+      'value' => '201',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'sid',
+      'operator' => '=',
+      'options' => '',
+      'value' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'category',
+      'operator' => '=',
+      'options' => '',
+      'value' => 'bug',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'priority',
+      'operator' => '=',
+      'options' => '',
+      'value' => '1',
+    ),
+  );
+  $view->exposed_filter = array (
+    array (
+      'tablename' => 'project_issue_project_node',
+      'field' => 'nid',
+      'label' => 'Project',
+      'optional' => '1',
+      'is_default' => '0',
+      'operator' => '1',
+      'single' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'sid',
+      'label' => 'Status',
+      'optional' => '1',
+      'is_default' => '0',
+      'operator' => '1',
+      'single' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'category',
+      'label' => 'Category',
+      'optional' => '1',
+      'is_default' => '0',
+      'operator' => '1',
+      'single' => '1',
+    ),
+    array (
+      'tablename' => 'project_issues',
+      'field' => 'priority',
+      'label' => 'Priority',
+      'optional' => '1',
+      'is_default' => '0',
+      'operator' => '1',
+      'single' => '1',
+    ),
+  );
+  $view->requires = array(project_issue_project_node, node, project_issues, users, project_issue_project_projects);
+  $views[$view->name] = $view;
+  return $views;
+}
\ No newline at end of file
