<?php

/**
 * @file
 * Functionality for the SuiteDesk Team module
 * Organized into the following sections:
 * - General hooks: (help), menu, perm, theme, views_api
 * - Node specific hooks: node_info, content_extra_fields, access, access_sql,
 *     storm_rewrite_where_sql, form, load, view, _beforesave, insert, update,
 *     delete, nodeapi
 * - Page generation: list, list_filter, list_filter_filter, list_filter_reset,
 *     autocomplete, autocomplete_combo
 */

/**
 * @function
 * Implementation of hook_help().
 */
/* function stormteam_help($path, $arg) {
}*/

/**
 * @function
 * Implementation of hook_menu().
 */
function stormteam_menu() {
  $items = array();

  $items['teams'] = array(
    'title' => 'Teams',
    'description' => 'SuiteDesk teams',
    'page callback' => 'stormteam_list',
    'access arguments' => array('Storm team: access'),
    'type' => MENU_NORMAL_ITEM,
    'weight' => 2,
  );

  $items['storm/teams/autocomplete'] = array(
    'title' => 'Teams autocomplete',
    'page callback' => 'stormteam_autocomplete',
    'access arguments' => array('Storm team: access'),
    'type' => MENU_CALLBACK,
  );

  return $items;
}

/**
 * @function
 * Implementation of hook_perm().
 */
function stormteam_perm() {
  return array(
    'Storm team: access',
    'Storm team: add',
    'Storm team: view all',
    'Storm team: view own',
    'Storm team: view belonged',
    'Storm team: edit all',
    'Storm team: edit own',
    'Storm team: edit belonged',
    'Storm team: delete all',
    'Storm team: delete own',
    'Storm team: delete belonged',
  );
}

/**
 * @function
 * Implementation of hook_theme().
 */
function stormteam_theme() {
  return array(
    'stormteam_list' => array(
      'file'      => 'stormteam.theme.inc',
      'arguments' => array('header', 'teams'),
    ),
    'stormteam_view' => array(
      'file'      => 'stormteam.theme.inc',
      'arguments' => array('node', 'teaser', 'page'),
    ),
  );
}

/**
 * @function
 * Implementation of hook_views_api().
 */
function stormteam_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'stormteam'),
  );
}

/**
 * @function
 * Implementation of hook_node_info().
 */
function stormteam_node_info() {
  return array(
    'stormteam' => array(
      'name' => t('Team'),
      'module' => 'stormteam',
      'description' => t('A team for use within the SuiteDesk project management application'),
      'title_label' => t('Team Name'),
      'body_label' => t('Description'),
    )
  );
}

/**
 * @function
 * Implementation of hook_content_extra_fields().
 */
function stormteam_content_extra_fields($type_name) {
  if ($type_name == 'stormteam') {
    return array(
      'group1' => array('label' => 'Team members', 'weight' => -20),
    );
  }
}

function stormteam_access($op, $node, $account = NULL) {
  if (empty($account)) {
    global $user;
    $account = $user;
  }

  if ($op == 'create') {
    return user_access('Storm team: add');
  }

  if (is_numeric($node)) {
    $node = node_load($node);
  }

  if ($op == 'delete') {
    if (user_access('Storm team: delete all')) {
      return TRUE;
    }
    elseif (user_access('Storm team: delete own') && ($account->uid == $node->uid)) {
      return TRUE;
    }
    elseif (user_access('Storm team: delete belonged') && stormteam_user_belongs_to_team($node->nid, $account->stormperson_nid)) {
      return TRUE;
    }
    elseif (user_access('Storm team: delete belonged') && stormteam_user_belongs_to_team($node->nid, $account->stormorganization_nid)) {
      return TRUE;
    }
  }

  if ($op == 'update') {
    if (user_access('Storm team: edit all')) {
      return TRUE;
    }
    elseif (user_access('Storm team: edit own') && ($account->uid == $node->uid)) {
      return TRUE;
    }
    elseif (user_access('Storm team: edit belonged') && stormteam_user_belongs_to_team($node->nid, $account->stormperson_nid)) {
      return TRUE;
    }
    elseif (user_access('Storm team: edit belonged') && stormteam_user_belongs_to_team($node->nid, $account->stormorganization_nid)) {
      return TRUE;
    }
  }

  if ($op == 'view') {
    if (user_access('Storm team: view all')) {
      return TRUE;
    }
    elseif (user_access('Storm team: view own') && ($account->uid == $node->uid)) {
      return TRUE;
    }
    elseif (user_access('Storm team: view belonged') && stormteam_user_belongs_to_team($node->nid, $account->stormperson_nid)) {
      return TRUE;
    }
    elseif (user_access('Storm team: view belonged') && stormteam_user_belongs_to_team($node->nid, $account->stormorganization_nid)) {
      return TRUE;
    }
  }

  return FALSE;
}

function stormteam_access_sql($sql, $where = array()) {
  if (!user_access('Storm team: view all')) {
    global $user;

    $cond = '';
    if (user_access('Storm team: view own')) {
      $cond .= 'n.uid = ' . $user->uid;
    }
    if (user_access('Storm team: view belonged')) {
      $cond .= !empty($cond) ? ' OR ' : '';
      $cond .= 'ste.mnid = ' . $user->stormperson_nid;
    }
    $where[] = empty($cond) ? '0 = 1' : $cond;
  }

  $where[] = "'storm_access' = 'storm_access'";
  return storm_rewrite_sql($sql, $where);
}

function stormteam_storm_rewrite_where_sql($query, $primary_table, $account) {
  static $conds = array();

  if (isset($conds[$primary_table][$account->uid])) {
    return $conds[$primary_table][$account->uid];
  }

  $cond = '';
  if (!preg_match("/'storm_access' = 'storm_access'/", $query)) {
    if (user_access('Storm team: view all', $account)) {
      return '';
    }
    if (user_access('Storm team: view own', $account)) {
      $cond .= " ${primary_table}.uid = " . $account->uid;
    }
    if (user_access('Storm team: view belonged', $account)) {
      $cond .= !empty($cond) ? ' OR ' : '';
      $cond .= "ste1.mnid = " . $account->stormperson_nid;
    }
    if ($cond) {
      $cond = " WHEN 'stormteam' THEN (SELECT DISTINCT 1 FROM {stormteam} ste1 WHERE ste1.vid = ${primary_table}.vid AND ($cond)) ";
    }
    else {
      $cond = " WHEN 'stormteam' THEN 0 ";
    }
  }
  $conds[$primary_table][$account->uid] = $cond;
  return $cond;
}

/**
 * @function
 * Implementation of hook_form().
 */
function stormteam_form(&$node) {
  $breadcrumb = array(
    l(t('SuiteDesk'), 'dashboard'),
    l(t('Teams'), 'teams'),
  );
  drupal_set_breadcrumb($breadcrumb);

  $type = node_get_types('type', $node);

  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#required' => TRUE,
    '#default_value' => $node->title,
    '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'title') : -21,
  );

  $form['group1'] = array(
    '#type' => 'markup',
    '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'group1') : -20,
  );

  $options = array(0 => '-');

  $s_per = "SELECT n.nid, n.title FROM {node} n INNER JOIN {stormperson} spe ON n.vid=spe.vid WHERE n.type='stormperson' AND n.status = 1 ORDER BY n.title";
  $s_per = stormperson_access_sql($s_per);
  $s_per = db_rewrite_sql($s_per);
  $r_per = db_query($s_per);
  $people = array();
  while ($person = db_fetch_object($r_per)) {
    $people[$person->nid] = $person->title;
  }

  $options = $options + array(-1 => t('-PEOPLE-')) + $people;

  $s_org = "SELECT n.nid, n.title FROM {node} n WHERE n.type='stormorganization' AND n.status = 1 ORDER BY n.title";
  $s_org = stormorganization_access_sql($s_org);
  $s_org = db_rewrite_sql($s_org);
  $r_org = db_query($s_org);
  $organizations = array();
  while ($organization = db_fetch_object($r_org)) {
    $organizations[$organization->nid] = $organization->title;
  }

  $options = $options + array(-2 => t('-ORGANIZATIONS-')) + $organizations;

  $i = 1;
  $variable = 'members_array_'. $i;

  // Add rows for existing team members
  if (isset($node->members_array) && is_array($node->members_array)) {
    foreach ($node->members_array as $node->$variable => $name) {
      $form['group1'][$variable] = array(
        '#type' => 'select',
        '#title' => t('Team Member @num', array('@num' => $i)),
        '#options' => $options,
        '#default_value' => $node->$variable,
      );
      $i++;
      $variable = 'members_array_'. $i;
    }
  }

  // Add placeholder rows
  for ($j = 0; $j < 3; $j++) {
    $variable = 'members_array_'. $i;
    $form['group1'][$variable] = array(
      '#type' => 'select',
      '#title' => t('Team Member @num', array('@num' => $i)),
      '#options' => $options,
      '#default_value' => (isset($node->$variable)) ? $node->$variable : NULL,
    );
    $i++;
  }

  if ($type->has_body) {
    $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
  }

  $form['title_old'] = array(
    '#type' => 'hidden',
    '#default_value' => (isset($node->title_old)) ? $node-title_old : NULL,
  );

  return $form;
}

/**
 * @function
 * Implementation of hook_load().
 */
function stormteam_load($node) {
  $result = db_query('SELECT ste.mnid, n.title, n.status FROM {stormteam} ste JOIN {node} n ON (ste.mnid = n.nid) WHERE ste.vid = %d', $node->vid);
  $additions = new stdClass();
  $additions->members_array = array();
  $additions->members_deactivated_array = array();
  while ($members = db_fetch_object($result)) {
    $additions->members_array[$members->mnid] = $members->title;
    if ($members->status == 0) {
      $additions->members_deactivated_array[$members->mnid] = $members->title;
    }
  }
  $additions->title_old = $node->title;
  return $additions;
}

/**
 * @function
 * Implementation of hook_view().
 */
function stormteam_view($node, $teaser, $page) {
  $breadcrumb = array(
    l(t('SuiteDesk'), 'dashboard'),
    l(t('Teams'), 'teams'),
  );
  drupal_set_breadcrumb($breadcrumb);

  return theme('stormteam_view', $node, $teaser, $page);
}

/**
 * @function
 * Function to be called internally before saving SuiteDesk Team nodes
 */
function _stormteam_beforesave(&$node) {
  $i = 1;
  $variable = 'members_array_'. $i;

  while (isset($node->$variable)) {
    $title = db_fetch_object(db_query('SELECT n.title FROM {node} AS n WHERE nid = %d', $node->$variable));
    if (!empty($title)) {
      $node->members_array[$node->$variable] = $title->title;
    }
    $i++;
    $variable = 'members_array_'. $i;
  }
  if (isset($node->members_array)) {
    if (array_key_exists(0, $node->members_array)) {
      unset($node->members_array[0]);
    }

    $node->members = serialize($node->members_array);
  }
}

/**
 * @function
 * Implementation of hook_insert().
 */
function stormteam_insert($node) {
  _stormteam_beforesave($node);
  if (isset($node->vid) && (isset($node->nid)) && (isset($node->members_array))) {
    $member = new stdClass();
    $member->nid = $node->nid;
    $member->vid = $node->vid;
    foreach ($node->members_array as $mnid => $member_name) {
      $member->mnid = $mnid;
      drupal_write_record('stormteam', $member);
    }
  }
}

/**
 * @function
 * Implementation of hook_update().
 */
function stormteam_update($node) {
  _stormteam_beforesave($node);

  if ($node->revision) {
    stormteam_insert($node);
  }
  else {

    if (isset($node->vid) && (isset($node->nid)) && (isset($node->members_array))) {
      // Delete the old members
      db_query("DELETE FROM {stormteam} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);

      // Insert entries
      $member = new stdClass();
      $member->nid = $node->nid;
      $member->vid = $node->vid;
      foreach ($node->members_array as $mnid => $member_name) {
        $member->mnid = $mnid;
        drupal_write_record('stormteam', $member);
      }
    }

    // Invokes hook_stormteam_change so that if title has changed, other modules that have cached the title can update it.
    if ($node->title != $node->title_old) {
      module_invoke_all('stormteam_change', $node->nid, $node->title);
    }
  }
}

/**
 * @function
 * Implementation of hook_delete().
 */
function stormteam_delete($node) {
  // Notice that we're matching all revision, by using the node's nid.
  db_query('DELETE FROM {stormteam} WHERE nid = %d', $node->nid);
}

/**
 * @function
 * Implementation of hook_nodeapi().
 */
function stormteam_nodeapi(&$node, $op, $teaser, $page) {
  if ($node->type != 'stormteam') {
    return;
  }
  switch ($op) {
    case 'delete revision':
      // Notice that we're matching a single revision based on the node's vid.
      db_query('DELETE FROM {stormorganization} WHERE vid = %d', $node->vid);
      break;
  }
}

/**
 * Before delete a team.
 */
function _stormteam_validate_predelete($node) {
  $nid = $node->nid;
  $items = array();

  if (_storm_validate_predelete('stormproject', "s.assigned_nid = $nid")) {
    $items[] = '<a href="/projects">' . t('projects') . '</a>';
  }
  if (_storm_validate_predelete('stormtask', "s.assigned_nid = $nid")) {
    $items[] = '<a href="/tasks">' . t('tasks') . '</a>';
  }
  if (_storm_validate_predelete('stormticket', "s.assigned_nid = $nid")) {
    $items[] = '<a href="/tickets">' . t('tickets') . '</a>';
  }

  $nitems = count($items);
  if ($nitems > 0) {
    $elements = $items[0];
    if ($nitems > 2) {
      for ($i = 1; $i < $nitems - 1; $i++) {
        $elements .= ', ' . $items[$i];
      }
    }
    if ($nitems > 1) {
      $elements .= ' ' . t('and') . ' ' . $items[$nitems - 1];
    }
    form_set_error('title', t('Impossible to remove due to existing !elements associated to this team!', array('!elements' => $elements)));
    return FALSE;
  }
  return TRUE;
}

/**
 * @function
 * Function to display list of SuiteDesk Teams, called from hook_menu
 */
function stormteam_list() {
  // Add SuiteDesk generic CSS
  drupal_add_css(drupal_get_path('module', 'storm') .'/storm.css', 'module');

  $breadcrumb = array(
    l(t('SuiteDesk'), 'dashboard'),
  );
  drupal_set_breadcrumb($breadcrumb);

  if (array_key_exists('name', $_GET)) {
    if ($_SESSION['stormteam_list_filter']['name'] != $_GET['name']) {
      $_SESSION['stormteam_list_filter']['name'] = $_GET['name'];
    }
  }

  $i = new stdClass();
  $i->type = 'stormteam';

  $header = array(
    array(
      'data' => t('Team Name'),
      'field' => 'n.title',
      'sort' => 'ASC',
    ),
    array(
      'data' => t('Description'),
      'field' => 'nre.body',
      'sort' => '',
    ),
    array(
      'data' => storm_icon_add_node($i, $_GET),
      'class' => 'storm_list_operations',
    ),
  );

  $s = "SELECT n.nid, n.title, n.type, nre.teaser, nre.format, n.uid FROM {node} AS n
    INNER JOIN {stormteam} AS ste ON n.vid = ste.vid
    INNER JOIN {node_revisions} AS nre ON n.vid = nre.vid
    WHERE n.status=1 AND n.type='stormteam'
    GROUP BY n.nid";

  $where = array();
  $args = array();
  $filterfields = array();

  if (isset($_SESSION['stormteam_list_filter']['name']) && $_SESSION['stormteam_list_filter']['name'] != '') {
    $where[] = "LOWER(n.title) LIKE LOWER('%%%s%%')";
    $args[] = $_SESSION['stormteam_list_filter']['name'];
    $filterfields[] = t('Team name');
  }

  $itemsperpage = isset($_SESSION['stormteam_list_filter']['itemsperpage']) ? $_SESSION['stormteam_list_filter']['itemsperpage'] : variable_get('storm_default_items_per_page', 10);

  if (count($filterfields) == 0) {
    $filterdesc = t('Not filtered');
  }
  else {
    $filterdesc = t('Filtered by !fields', array('!fields' => implode(", ", array_unique($filterfields))));
  }
  $filterdesc .= ' | '. t('!items items per page', array('!items' => $itemsperpage));

  $o = drupal_get_form('stormteam_list_filter', $filterdesc);
  $s = stormteam_access_sql($s, $where);
  $s = db_rewrite_sql($s);
  $tablesort = tablesort_sql($header);
  // default count replacement doesn't work, because we need the distinct
  $count_query = preg_replace('/SELECT.*?FROM /As', 'SELECT COUNT(DISTINCT(n.nid)) FROM ', $s);
  $r = pager_query($s . $tablesort, $itemsperpage, 0, $count_query, $args);
  $rows = array();
  while ($team = db_fetch_object($r)) {
    $rows[] = array(
      l($team->title, 'node/'. $team->nid),
      check_markup($team->teaser),
      array(
        'data' => storm_icon_edit_node($team, $_GET) .'&nbsp;'. storm_icon_delete_node($team, $_GET),
        'class' => 'storm_list_operations',
      ),
    );
  }

  $o .= theme('table', $header, $rows, array('id' => 'stormteams'));
  $o .= theme('pager', NULL, $itemsperpage, 0);
  print theme('page', $o);
}

/**
 * @function
 * Function to define form for setting SuiteDesk Team list filter.
 */
function stormteam_list_filter(&$form_state, $filterdesc = 'Filter') {
  $name = isset($_SESSION['stormteam_list_filter']['name']) ? $_SESSION['stormteam_list_filter']['name'] : '';

  $itemsperpage = isset($_SESSION['stormteam_list_filter']['itemsperpage']) ? $_SESSION['stormteam_list_filter']['itemsperpage'] : variable_get('storm_default_items_per_page', 10);
  $_SESSION['stormteam_list_filter']['itemsperpage'] = $itemsperpage;

  $form = array();

  $form['filter'] = array(
    '#type' => 'fieldset',
    '#title' => $filterdesc,
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#weight' => -20,
  );

  $form['filter']['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => $name,
    '#autocomplete_path' => 'storm/teams/autocomplete',
  );

  $form['filter']['group1'] = array(
    '#type' => 'markup',
    '#theme' => 'storm_form_group',
    '#attributes' => array('class' => 'formgroup-submit'),
  );

  $form['filter']['group1']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Filter'),
    '#submit' => array('stormteam_list_filter_filter'),
  );

  $form['filter']['group1']['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Reset'),
    '#submit' => array('stormteam_list_filter_reset'),
  );

  $form['filter']['group1']['itemsperpage'] = array(
    '#type' => 'textfield',
    '#title' => t('Items'),
    '#size' => 10,
    '#default_value' => $itemsperpage,
    '#prefix' => '<div class="container-inline">',
    '#suffix' => '</div>',
  );

  return $form;
}

/**
 * @function
 * Function to define submit handler in form for setting SuiteDesk Team list filter.
 */
function stormteam_list_filter_filter($form, &$form_state) {
  $_SESSION['stormteam_list_filter']['name'] = $form_state['values']['name'];
  $_SESSION['stormteam_list_filter']['itemsperpage'] = $form_state['values']['itemsperpage'];
}

/**
 * @function
 * Function to define reset handler in form for setting SuiteDesk Team list filter
 */
function stormteam_list_filter_reset($form, &$form_state) {
  unset($_SESSION['stormteam_list_filter']);
}

/**
 * @function
 * Function to return an autocomplete list for SuiteDesk Teams
 */
function stormteam_autocomplete($string = '') {
  $matches = array();
  if ($string) {
#   $s = "SELECT title FROM {node} AS n WHERE n.type='stormteam' AND LOWER(title) LIKE LOWER('%%%s%%')";
    $s = "SELECT n.title FROM {node} n INNER JOIN {stormteam} ste ON n.vid=ste.vid WHERE n.type='stormteam' AND n.status=1 AND LOWER(n.title) LIKE LOWER('%%%s%%')";
    $s = stormteam_access_sql($s);
    $s = db_rewrite_sql($s);

    $result = db_query_range($s, $string, 0, 10);
    while ($team = db_fetch_object($result)) {
      $matches[$team->title] = check_plain($team->title);
    }
  }

  drupal_json($matches);
}

/**
 * @function
 * Function to return an autocomplete list for SuiteDesk Teams and People
 */
function stormteam_autocomplete_combo($string = '') {
  $matches = array();
  if ($string) {
    // TO BE COMPLETED
    $s = "SELECT title FROM {node} AS n WHERE n.type='stormteam' AND LOWER(title) LIKE LOWER('%s%%')";
    $s = stormteam_access_sql($s);
    $s = db_rewrite_sql($s);

    $result = db_query_range($s, $string, 0, 10);
    while ($obj = db_fetch_object($result)) {
      $matches[$obj->title] = check_plain($obj->title);
    }
  }

  drupal_json($matches);
}

/**
 * @function
 * Returns whether user is in a SuiteDesk team based on team $node and user $account
 */
function stormteam_user_belongs_to_team($team, $person_or_organization_nid) {
  $node = node_load($team);

  // Check for person_or_organization_nid in the team members array and return TRUE / FALSE
  if (is_array($node->members_array)) {
    return array_key_exists($person_or_organization_nid, $node->members_array);
  }
  else {
    return FALSE;
  }
}

/**
 * @function
 * Function to return the teams that a user is a member of
 */
function stormteam_user_return_teams($account = NULL) {
  if (empty($account)) {
    global $user;
    $account = $user;
  }

  // Get the list of team nodes. Include organizations which the person is a member of.
  $s = "SELECT ste.nid FROM {stormteam} ste JOIN {stormperson} stp ON (stp.nid = ste.mnid OR stp.organization_nid = ste.mnid) WHERE stp.user_uid = %d";
  $r = db_query($s, $account->uid);
  $teams = array();
  while ($team = db_fetch_object($r)) {
    $teams[] = $team->nid;
  }

  return $teams;
}

function stormteam_storm_dashboard_links($type) {
  $links = array();
  if ($type == 'page' || $type == 'block') {
    $links[] = array(
      'theme' => 'storm_dashboard_link',
      'title' => t('Teams'),
      'icon' => 'stormteam-item',
      'path' => 'teams',
      'params' => array(),
      'access_arguments' => 'Storm team: access',
      'node_type' => 'stormteam',
      'add_type' => 'stormteam',
      'map' => array(),
      'weight' => 3,
    );
  }
  return $links;
}
