<?php
// Wishlist management module for Drupal
// Written by Scott McLewin and Melanie Paul-McLewin
// drupal AT mclewin DOT com


/**
 * Hook to return permission supported by this module
 *
 * @return
 *   create wishlists  - authority to do "create content -> wishlist"
 *   edit own wishlist - authority to edit your own wishlist
 *   admin wishlist    - authority to administer the wishlist module
 *   access wishlists  - rights to view, purchase and return items on wishlists
 */
function wishlist_permission() {
  return array(
    "create wishlists" => array(
      'title' => t("create wishlists"),
      'description' => t('authority to create wishlist nodes'),
    ),
    "edit own wishlist" => array(
      'title' => t("edit own wishlist"),
      'description' => t('authority to edit your own wishlist'),
    ),
    "admin wishlist" => array(
      'title' => t("admin wishlist"),
      'description' => t('User is allowed to administer the wishlist module settings'),
    ),
    "access wishlists" => array(
      'title' => t("access wishlists"),
      'description' => t('Rights to view, purchase and return items on wishlists'),
    ),
    'reveal purchase status' => array(
      'title' => t('reveal purchase status'),
      'description' => t('Allows the user to reveal who purchased items off their list'),
    ),
  );
}


/**
 * load hook to bring in the wishlist specific node fields
 *
 * @param $node
 *   Core node record entries
 * @return
 *   A wishlist object, which will be combined with $node
 */
function wishlist_load($nodes) {
  foreach ($nodes as $node) {
    //$result = db_query('SELECT * FROM {wishlist} WHERE nid = :nid', array(':nid' => $node->nid));
	$query = db_select('wishlist', 'w');
	$query->fields('w');
	$query->condition('w.nid', $node->nid);
	$result = $query->execute();
    $wishlist = $result->fetchObject();
    
    $wishlist->item_quantity_purchased = _wishlist_get_node_quantity_purchased($node->nid);

    foreach ($wishlist as $property => $value) {
      $nodes[$node->nid]->$property = $value;
    }    
  }
}


/**
 * node_name hook to return this module's name for nodes it creates
 *
 * @return
 *   Name of the node type created by this module
 */
function wishlist_node_name() {
  return 'wishlist';
}



/**
 * Implementation of hook_node_info().
 */
function wishlist_node_info() {
  $items = array(
    'wishlist' => array(
      'name' => t('Wishlist'),
      'base' => 'wishlist',
      'description' => t('A wishlist allows you maintain a collection of items you want for a special occasion, such as a holiday, birthday or just for the fun of it.  The wishlist is just a list, to purchase items users will be directed to the appropriate ecommerce site to complete the purchase.'),
      'has_title' => TRUE,
      'title_label' => t('Title'),
      'help' => t('Describe the item you want to add to your wishlist.  Include an estimated cost, the quantity you want and one or two URLs to sites that sell the item.'),
    ),
  );
  return $items;
}

function wishlist_field_extra_fields() {
  $extra['node']['wishlist'] = array (
    'form' => array(
        'item_is_public' => array(
          'label' => t('Public wishlist item'),
          'description' => t('Makes this item visible to anybody. If you uncheck this only you will be able to view the entry and this node will be set to \'unpublished\'.'),
          'weight' => 7,
        ),
        'item_est_cost' => array(
          'label' => t('Estimated Cost'),
          'description' => t('If you know about how much the item will cost, enter it here.'),
          'weight' => 1,
        ),
        'item_url1' => array(
          'label' => t('Primary URL'),
          'description' => t('Enter the URL from which the item can be purchased. Use the form \'http://www.example.com\''),
          'weight' => 5,
        ),
        'item_url2' => array(
          'label' => t('Secondary URL'),
          'description' => t('Optionally enter another URL from which the item can be purchased. Use the form \'http://www.example.com\''),
          'weight' => 6,
        ),
        'item_quantity_requested' => array(
          'label' => t('Quantity'),
          'description' => t('Enter the number of this item that you would like if one is not enough.'),
          'weight' => 3,
        ),
        'item_currency' => array(
          'label' => t('Currency'),
          'description' => t('Enter the three character ISO currency code for the value in the Estimated Cost field. e.g. USD for US Dollars, THB for Thai Baht, etc'),
          'weight' => 2,
        ),
        'item_priority' => array(
          'label' => t('Priority'),
          'description' => t('Rate the importance of this item to help your friends and family select a gift.'),
          'weight' => 4,
        )
     ),
     'display' => array(
        'wishlist_content' => array(
          'label' => t('Wishlist Field Group'),
          'description' => t('All wishlist related fields in a two column group'),
          'weight' => 10,
        )
     )
  );
  
  return $extra;
}


/**
 * @desc Register the wishlist module's theme functions
 */
function wishlist_theme() {
  return array(
    'wishlist_username' => array(
      'variables' => array('uid' => NULL, 'defaultname' => NULL),
    ),
    'wishlist_node_url' => array(
      'variables' => array('url' => NULL, 'node' => NULL),
    ),
    'wishlist_cost' => array(
      'variables' => array('item_est_cost' => 0, 'item_currency' => variable_get('wishlist_default_currency', 'USD')),
    ),
    'wishlist_priority' => array(
      'variables' => array('item_priority' => 1),
    ),
    'wishlist_requested' => array(
      'variables' => array('item_quantity_requested' => 1),
    ),
    'wishlist_purchased' => array(
      'variables' => array('item_quantity_purchased' => 0, 'node' => NULL),
    ),
    'wishlist_last_updated' => array(
      'variables' => array('changed' => NULL),
    ),
    'wishlist_purchased_items_table' => array(
      'variables' => array('node' => NULL),
    ),
    'wishlist_items_user_purchased' => array(
      'variables' => array('node' => NULL),
    ),
    'wishlist_content' => array(
      'template' => 'wishlist-content--node',
      'variables' => array('wishlist_cost' => NULL, 'wishlist_url1' => NULL, 'wishlist_url2' => NULL, 
                           'wishlist_priority' => NULL, 'wishlist_requested' => NULL, 'wishlist_purchased' => NULL,
                           'wishlist_last_updated' => NULL, 'wishlist_reveal_form' => NULL, 'wishlist_purchased_items_table' => NULL,
                           'wishlist_items_user_purchased' => NULL),
    )
  );
}



  
/**
 * Help hook
 *
 * @param $section
 *   Which help URL has been requested
 * @return
 *   Help text to display
 */
function wishlist_help($path, $arg) {
  $output = "";

  switch ($path) {
    case 'admin/help#wishlist':
      break;
    case 'node/add#wishlist':
      $output = t("Add an item to one of your wishlist categories.");
      break;
  }
  return $output;
}


/**
 * Access hook for the wishlist module
 *
 * @param $op
 *   Operation the user is requesting
 * @param $node
 *   Node on which the user is requesting the operation
 * @param $account
 *   User's account
 * @return
 *   NODE_ACCESS_ALLOW / NODE_ACCESS_DENY / NODE_ACCESS_IGNORE based on checking permissions
 */
function wishlist_node_access($node, $op, $account) {

  if ($op == 'create') {
    return user_access('create wishlists', $account);
  }

  if ($op == 'update' || $op == 'delete' || $op == 'view') {
    if (user_access('edit own wishlist', $account) && ($account->uid == $node->uid)) {
      return NODE_ACCESS_ALLOW;
    }
  }

  if (isset($node->item_is_public) && (!$node->item_is_public) && ($account->uid != $node->uid)) {
    return NODE_ACCESS_DENY;
  }
  return NODE_ACCESS_IGNORE;
}

/**
 * Generate the content block that describes the links for the wishlist module
 * @param unknown_type $node
 */
function _wishlist_links($node) {
  global $user;
  $links = array();

    // Don't display a redundant edit link if they are node administrators.
  if ((user_access('edit own wishlist', $user) && ($user->uid == $node->uid)) && !user_access('administer nodes')) {
    $links['wishlist_edit_this_item'] = array(
      'title' => t('Edit this wishlist item'),
      'href' => "node/$node->nid/edit",
      'attributes' => array('title' => t('Edit your wishlist item')),
    );
  }

  // If the user has access rights to wishlists, then add links to handle purchases
  if (user_access('access wishlists') && !_wishlist_hide_purchase_info($node)) {
    // Only place the purchase link up if there are any items left
    if ($node->item_quantity_purchased < $node->item_quantity_requested) {
      $links['wishlist_get_this_gift'] = array(
        'title' => t('Get this gift'),
        'href' => "wishlist/item/$node->nid/purchase",
        'attributes' => array('title' => t('Mark this gift off the list so nobody else gets it')),
      );
    }
  }
  elseif (!$user->uid) {
    // If the user is not logged in, then give them the opportunity to do so.
    $links['wishlist_login_or_register'] = array(
      'title' => t('<a href="@login">Login</a> or <a href="@register">register</a> to purchase wishlist items', array('@login' => url('user/login'), '@register' => url('user/register'))),
      'html' => TRUE,
    );
  }
  
  
  return $links;
}

/**
 * Cron hook for wishlists.  Cleans up purchased wishlist nodes and moves older items into the user's private list.
 * @see http://drupal.org/node/1354
 */
function wishlist_cron() {
  $expire_days = variable_get('wishlist_item_expire_days', 30);
  // If the automatic expiration feature is disabled, return.
  if ($expire_days == 0) {
    return;
  }

  // Calculate the time before which we will go looking for items that are fully purchased.
  $time = REQUEST_TIME - ($expire_days * 24 * 60 * 60);

  watchdog('wishlist', "Hiding items purchased before @dt", array('@dt' => format_date($time)));

  // Pull back all of the items that have been fully purchased where the last
  // transaction was over 'wishlist_item_expire_days' ago and is public.  Move
  // these to the user's private list.  Do this in batches of 100.
  $query = db_select('wishlist_purchased', 'p');
  $query->join('node', 'n', 'n.nid = p.wishlist_purch_nid');
  $query->join('wishlist', 'w', 'n.nid = w.nid');
  $query->condition('w.item_is_public', 1);
  $query->addExpression('MAX(p.purch_date)', 'compare_date');
  $query->addExpression('w.item_quantity_requested - SUM(p.wishlist_purch_quantity)', 'remaining');
  $query->fields('n', array('title', 'nid'));
  $query->groupBy('n.nid');
  $query->havingCondition('remaining', 0, '<=');
  $query->havingCondition('compare_date', $time, '<');
  $query->orderBy('compare_date');
  $query->range(0,99);
  $result = $query->execute();

  while ($row = $result->fetchObject()) {
    watchdog('wishlist', 'Hiding purchased item @title.  It now appears only on the user\'s private list.', array(
      '@title' => $row->title,
    ));
    
    db_update('wishlist')
      ->fields(array(
        'item_is_public' => 0,
      ))
      ->condition('nid', $row->nid)
      ->execute();
  }
}

/**
 * Wishlist node specific form edit/entry generation through hook_form.
 *
 * @param $node
 *   See hook_form on http://www.drupaldocs.org for parameter details
 * @return
 *   See hook_form on http://www.drupaldocs.org for return value details
 */
function wishlist_form($node, &$form_state) {
  //
  // Basic structure from http://api.drupal.org/api/examples/node_example--node_example.module/function/node_example_form/6
  //
  // The site admin can decide if this node type has a title and body, and how
  // the fields should be labeled. We need to load these settings so we can
  // build the node form correctly.
  $type = node_type_get_type($node);

  if ($type->has_title) {
    $form['title'] = array(
      '#type' => 'textfield',
      '#title' => check_plain($type->title_label),
      '#required' => TRUE,
      '#default_value' => $node->title,
      '#weight' => -5,
    );
  }


  // IF the item_priority field is set to 0 then assume this is a newly initialized
  // record and set reasonable defaults on other entries as well.
  if (!isset($node->item_priority) || $node->item_priority == 0) {
    $node->item_priority = 3;
    $node->item_is_public = TRUE;
  }

  if (!isset($node->item_currency) || $node->item_currency == "") {
    $node->item_currency = variable_get("wishlist_default_currency", "USD");
  }


  $form["item_is_public"] = array(
    '#type' => 'checkbox',
    '#title' => t("Public wish list item"),
    '#return_value' => TRUE,
    '#default_value' => $node->item_is_public,
    '#description' => t("Makes this item visible to anybody.  If you uncheck this only you will be able to view the entry and this node will be set to 'unpublished'."),
  );

  // If the admin has hidden the currency field then add the currency to the
  // title of the estimated cost field.
  if (FALSE == variable_get("wishlist_show_currency", TRUE)) {
    $est_cost_currency = t(' (in @currency)', array('@currency' => $node->item_currency));
  }
  else {
    $est_cost_currency = '';
  }

  $form["item_est_cost"] = array(
    '#type' => 'textfield',
    '#title' => t("Estimated Cost") . $est_cost_currency,
    '#default_value' => isset($node->item_est_cost) ? $node->item_est_cost : '0',
    '#size' => 5,
    '#maxlength' => 5,
    '#description' => t("If you know about how much the item will cost, enter it here."),
  );

  if (variable_get("wishlist_show_currency", TRUE)) {
    $form["item_currency"] = array(
      '#type' => 'textfield',
      '#title' => t("Currency"),
      '#default_value' => $node->item_currency,
      '#size' => 5,
      '#maxlength' => 5,
      '#description' => t("Enter the three character ISO currency code for the value in the Estimated Cost field. e.g. USD for US Dollars, THB for Thai Baht, etc"),
    );
  }
  else {
    // If the currency value is not shown we still need to pass through
    // the default.  Do that through a hidden field.
    $form["item_currency"] = array(
      '#type' => 'hidden',
      '#default_value' => isset($node->item_currency) ? $node->item_currency : 'USD',
      '#size' => 5,
      '#maxlength' => 5,
    );
  }
  $form["item_url1"] = array(
    '#type' => 'textfield',
    '#title' => t("Primary URL"),
    '#default_value' => isset($node->item_url1) ? $node->item_url1 : '',
    '#size' => 60,
    '#maxlength' => 250,
    '#description' => t("Enter the URL from which the item can be purchased.  Use the form 'http://www.example.com'"),
  );

  $form["item_url2"] = array(
    '#type' => 'textfield',
    '#title' => t("Secondary URL"),
    '#default_value' => isset($node->item_url2) ? $node->item_url2 : '',
    '#size' => 60,
    '#maxlength' => 250,
    '#description' => t("Optionally enter another URL from which the item can be purchased.  Use the form 'http://www.example.com'"),
  );

  $form["item_quantity_requested"] = array(
    '#type' => 'textfield',
    '#title' => t("Quantity Requested"),
    '#default_value' => isset($node->item_quantity_requested) ? $node->item_quantity_requested : '1',
    '#size' => 5,
    '#maxlength' => 5,
    '#description' => t("Enter the number of this item that you would like if one is not enough"),
  );

  $options = _wishlist_get_item_priority_array();

  $form["item_priority"] = array(
    '#type' => 'select',
    '#title' => t("Priority"),
    '#default_value' => $node->item_priority,
    '#options' => $options,
    '#description' => t("Rate the importance of this item to help your friends and family select a gift"),
    '#extra' => FALSE,
  );

  return $form;
}

/**
 * Implementation of wishlist_validate().
 *
 * Our "quantity" field requires a number to be entered. This hook lets
 * us ensure that the user entered an appropriate value before we try
 * inserting anything into the database.
 *
 * Errors should be signaled with form_set_error().
 */
function wishlist_validate(&$node) {
  // We allow 0 as a quantity for placeholder wishlist items, such as pointers to a wishlist
  // on a shopping web site.
  if ($node->item_quantity_requested == '') {
    form_set_error('item_quantity_requested', t('Please enter the quantity requested.'));
  }
  elseif (!is_numeric($node->item_quantity_requested)) {
    form_set_error('item_quantity_requested', t('The requested quantity must be a number.'));
  }
  elseif ($node->item_quantity_requested < 0) {
    form_set_error('item_quantity_requested', t('The requested quantitiy cannot be negative.'));
  }

  if ($node->item_est_cost == '') {
    form_set_error('item_est_cost', t('You must enter a cost for this wishlist item'));
  }
  elseif (!is_numeric($node->item_est_cost)) {
    form_set_error('item_est_cost', t('You must enter a numeric value for the cost.'));
  }
}



/**
 * Implementation of wishlist_insert().
 *
 * As a new node is being inserted into the database, we need to do our own
 * database inserts.
 */
function wishlist_insert($node) {
  $node->item_url1 = _wishlist_correct_url($node->item_url1);
  $node->item_url2 = _wishlist_correct_url($node->item_url2);

  $id = db_insert('wishlist')
  ->fields(array(
    'nid' => $node->nid,
    'item_is_public' => $node->item_is_public,
    'item_est_cost' => $node->item_est_cost,
    'item_url1' => $node->item_url1,
    'item_url2' => $node->item_url2,
    'item_quantity_requested' => $node->item_quantity_requested,
    'item_priority' => $node->item_priority[0],
    'item_currency' => $node->item_currency,
  ))
  ->execute();

}

/**
 * Implementation of wishlist_update().
 *
 * As an existing node is being updated in the database, we need to do our own
 * database updates.
 */
function wishlist_update($node) {
  $node->item_url1 = _wishlist_correct_url($node->item_url1);
  $node->item_url2 = _wishlist_correct_url($node->item_url2);
  db_update('wishlist')
  ->fields(array(
    'item_is_public' => $node->item_is_public,
    'item_est_cost' => $node->item_est_cost,
    'item_url1' => $node->item_url1,
    'item_url2' => $node->item_url2,
    'item_quantity_requested' => $node->item_quantity_requested,
    'item_currency' => $node->item_currency,
    'item_priority' => $node->item_priority[0],
  ))
  ->condition('nid', $node->nid)
  ->execute();
}




/**
 * Implementation of wishlist_delete().
 *
 * When a node is deleted, we need to clean up related tables.
 */
function wishlist_delete($node) {
    db_delete('wishlist_purchased')
  ->condition('wishlist_purch_nid', $node->nid)
  ->execute();

  db_delete('wishlist')
  ->condition('nid', $node->nid)
  ->execute();
}


/**
 * @desc Hook_init implementation
 *
 * Add the wishlist css file to the page.
 */
function wishlist_init() {
  drupal_add_css(drupal_get_path('module', 'wishlist') . '/wishlist.css');
}

/**
 * hook_menu implementation
 *
 * @param
 *   See hook_menu in http://www.drupaldocs.org for parameter details
 * @return
 *   See hook_menu in http://www.drupaldocs.org for return code details
 */
function wishlist_menu() {
  $items['admin/config/content/wishlist'] = array(
    'title' => 'Wishlist settings',
    'description' => 'Configure the wishlist module\'s settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('wishlist_admin_settings'),
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'wishlist.admin.inc',
  );
  $items['wishlist/item/%/return/%'] = array(
    'title' => 'Wish lists',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('_wishlist_item_action_return', 2, 4),
    'access arguments' => array('access wishlists'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'wishlist.page.inc',
  );

  $items['wishlist/%'] = array(
    'title' => 'Wish lists',
    'page callback' => 'wishlist_page',
    'access arguments' => array('access wishlists'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'wishlist.page.inc',
  );
  $items['wishlist'] = array(
    'title' => 'Wish lists',
    'page callback' => 'wishlist_page',
    'access arguments' => array('access wishlists'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'wishlist.page.inc',
  );
  $items['admin/reports/wishlist'] = array(
    'title' => 'Wishlist purchases',
    'page callback' => 'wishlist_admin_page',
    'access arguments' => array('admin wishlist'),
    'description' => 'Manage and view the wishlist purchase records for your site.',
    'type' => MENU_NORMAL_ITEM,
    'file' => 'wishlist.admin.inc',
  );
  $items['admin/config/content/wishlist/delete/%/%'] = array(
    'title' => 'Delete wishlist item',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('_wishlist_admin_delete_item', 5, 6),
    'access arguments' => array('admin wishlist'),
    'type' => MENU_CALLBACK,
    'file' => 'wishlist.admin.inc',
  );
  return $items;
}

/**
 * @desc use hook_menu_alter to put the proper permissions on the wishlist add menu
 */
function wishlist_menu_alter(&$callbacks) {
  $callbacks['node/add/wishlist']['access callback'] = 'user_access';
  $callbacks['node/add/wishlist']['access arguments'] = array('create wishlists');
}




/**
 * Implements hook_block_info().
 */
function wishlist_block_info() {
  if (TRUE) {
    $blocks['wishlist-lists']["info"] = t("Wishlists");
    return $blocks;
  }
}

/**
 * Implements hook_block_view().
 */
function wishlist_block_view($delta) {
  $block = array();
  
  switch($delta) {
    case 'wishlist-lists':
      $query = db_select('node', 'n');
      $query->join('users', 'u', 'n.uid = u.uid');
      $query->condition('n.type', 'wishlist')
        ->addTag('node_access') 
        ->fields('n', array('nid', 'title', 'uid'))
        ->fields('u', array('name'))
        ->groupBy('u.uid')
        ->orderBy('u.name', 'DESC');
      $result = $query->execute();  
      
      if($result->rowCount() == 0) {
        // If the site has no wishlists, then return an empty block
        $block['subject'] = '';
        $block['content'] = '';  
      } else {
        $items = array();
        while ($record = $result->fetchObject()) {
          $items[] = format_wishlists($record);
        }
        $block_content = theme('item_list', array('items' => $items));
    
        // add a more link to our page that displays all the links
        $block_content .=
             "<div class='more-link'>" .
             l(t("More"), "wishlist", array('attributes' => array("title" => t("View all wishlists."))))
             . "</div>";
    
        $block["subject"] = t("Wishlists");
        $block["content"] = $block_content;        
      }
  }
  
  return $block;
}

/**
 * Implements hook_user_view().
 */
function wishlist_user_view($account, $view_mode) {
  $edit_user->content['wishlist'] = array(
    '#value' => l(t("View !user's wishlist", array('!user' => $account->name)), 'wishlist/' . $account->uid),
    '#weight' => 11,
  );
}


/**
 * Allows for a site to theme the username.  An example reason to do this
 * would be to display the user's real name from a site specific profile system
 * rather than to use the name in the Drupal user record.  The output will be
 * truncated to 15 characters.  It should contain no markup.
 *
 */
function theme_wishlist_username($variables) {
  $uid = $variables['uid'];
  $defaultname = $variables['defaultname'];
  return $defaultname;
}

/**
 * Formats the wishlist item title with (or without) appropriate links to the full record
 *
 * @param $object
 *   The wishlist node to format
 * @param $append_text
 *   Optional text to tack on the end of the user's name.
 * @return
 *   Formatted text ready for display
 */
function format_wishlists($object, $append_text = "") {
  global $user;

  if ($object->uid && $object->name) {
    // If this configuration has a token replacement set, then grab the user record and 
    // pass it off for token processing.  If not, then don't bother with the overhead 
    // of reading in the user record details for each wishlist username display.
    if(variable_get('wishlist_user_name_token', '') != '') {
       $item_user_account = user_load($object->uid);
       $wishlist_entry_defaultname = _wishlist_user_name_from_tokens($item_user_account);
    } else {
      $wishlist_entry_defaultname = $object->name;
    }
    
    $fullname = theme('wishlist_username', array('uid' => $object->uid, 'defaultname' => $wishlist_entry_defaultname));

    // Shorten the name when it is too long or it will break many tables.
    if (drupal_strlen($fullname) > 20) {
      $name = truncate_utf8($fullname, 15) . '...';
    }
    else {
      $name = $fullname;
    }

    $output = l($name . $append_text, 'wishlist/' . $object->uid, array('title' => t('View wishlist items for @user', array('@user' => $fullname))));
  }
  elseif ($object->name) {
    // Sometimes modules display content composed by people who are
    // not registered members of the site (e.g. mailing list or news
    // aggregator modules). This clause enables modules to display
    // the TRUE author of the content.
    if ($object->homepage) {
      $output = '<a href="' . $object->homepage . '">' . check_plain($object->name . $append_text) . '</a>';
    }
    else {
      $output = check_plain($object->name . $append_text);
    }

    $output .= ' (' . t('not verified') . ')';
  }
  else {
    $output = variable_get('anonymous', t('Anonymous'));
  }

  // If the curent user is logged in, then also look for new items.
  if ($user->uid && $object->uid) {
    $query = db_select('node', 'n');
    $query->join('users', 'u', 'n.uid = u.uid');
    $query->leftJoin('history', 'h', 'n.nid = h.nid AND h.uid = :uid', array(':uid' => $user->uid));
    $query->addExpression('COUNT(n.nid)', 'count');
    $query->condition('n.status', '1')
          ->condition('u.uid', $user->uid)
          ->condition('n.type', 'wishlist')
          ->condition('n.created', NODE_NEW_LIMIT, '>')
          ->isNull('h.nid')
          ->addTag('node_access');
    $new_count=$query->execute()->fetchField();
          
    if ($new_count > 0) {
      $output .= '<span class="new-wishlist-items">' . t('@count new', array('@count' => $new_count)) . '</span>';
    }
  }
  return $output;
}






/**
 * hook_view implementation
 *
 * @param
 *   See hook_view in http://www.drupaldocs.org for parameter details
 * @return
 *   See hook_view in http://www.drupaldocs.org for return code details
 */
function wishlist_view($node, $view_mode) {

  if ($view_mode == 'full' && node_is_page($node)) {
    $giftee_account = user_load($node->uid);

    // Build giftee name from tokens if possible, otherwise $giftee_account->name
    $giftee_name = _wishlist_user_name_from_tokens($giftee_account);

    $breadcrumb[] = l(t('Home'), NULL);
    $breadcrumb[] = l(t('All wishlists'),   'wishlist');
    $breadcrumb[] = l(t("@name's wishlist", array('@name' => check_plain($giftee_name))), 'wishlist/' . $node->uid);
    drupal_set_breadcrumb($breadcrumb);
  }
  
  $node->content['wishlist_content'] = array(
    '#theme' => 'wishlist_content',
    '#wishlist_cost' => theme('wishlist_cost', array('item_est_cost' => $node->item_est_cost, 'item_currency' => $node->item_currency)),
    '#wishlist_url1' => theme('wishlist_node_url', array('url' => $node->item_url1, 'node' => $node)),
    '#wishlist_url2' => theme('wishlist_node_url', array('url' => $node->item_url2, 'node' => $node)),
    '#wishlist_priority' => theme('wishlist_priority', array('item_priority' => $node->item_priority)),
    '#wishlist_requested' => theme('wishlist_requested', array('item_quantity_requested' => $node->item_quantity_requested)),
    '#wishlist_purchased' => theme('wishlist_purchased', array('item_quantity_purchased' => $node->item_quantity_purchased, 'node' => $node)),
    '#wishlist_last_updated' => theme('wishlist_last_updated', array('changed' => $node->changed)),
    '#wishlist_reveal_form' => _wishlist_get_reveal_form($node->uid),
    '#wishlist_purchased_items_table' => theme('wishlist_purchased_items_table', array('node' => $node)),
    '#wishlist_items_user_purchased' => theme('wishlist_items_user_purchased', array('node' => $node)),
    '#weight' => 10,
  );
  
  return $node;
}

function wishlist_node_view($node, $view_mode) {
  if (($node->type == 'wishlist') && ($view_mode != 'rss')) {
    $node->content['links']['wishlist'] = array(
      '#theme' => 'links',
      '#links' => _wishlist_links($node),
      '#attributes' => array('class' => array('links', 'inline')),
    );
    
  }
}



function theme_wishlist_cost($variables) {
  return '<div class="item_est_cost">' . t("Estimated Cost") . " " . _wishlist_currency_str($variables['item_currency']) . $variables['item_est_cost'] . '</div>';
}


function theme_wishlist_priority($variables) {
  $priority_str = _wishlist_get_item_priority_array();
  return '<div class="priority lessimportant">' . $priority_str[$variables['item_priority']] . '</div>';
  
}
function theme_wishlist_requested($variables) {
  return '<div class="requested lessimportant">' . t('Requested') . ' ' . $variables['item_quantity_requested'] . '</div>';
  
}
function theme_wishlist_purchased($variables) {
  $node = $variables['node'];
  
  if (_wishlist_hide_purchase_info($node)) {
    return '<div class="purchased lessimportant">' . t('Purchase details hidden') . '</div>';
  }
  else {
    return '<div class="purchased lessimportant">' . t('Purchased') . ' ' . $variables['item_quantity_purchased'] . '</div>';
  }
}

function theme_wishlist_last_updated($variables) {
  return "<div class='lastchanged'>" . t("Last updated on") . " " . format_date($variables['changed'], 'short') . "</div>";
}


function theme_wishlist_purchased_items_table($variables) {
  $node = $variables['node'];

  return _wishlist_render_purchased_items($node);
}


function theme_wishlist_items_user_purchased($variables) {
  $node = $variables['node'];
  global $user;
  
  $output = '';
  
  // Under a preview of a new node there is nothing to see and no nid value just yet.  Skip this code
  if (!is_NULL($node->nid) && $node->nid > 0) {
    // Retrieve all of the records showing what the currently logged in user purchaed on this item.
    $result = db_query("SELECT p.wishlist_purch_wid, p.wishlist_purch_buyer_uid, p.wishlist_purch_quantity FROM {wishlist_purchased} p WHERE p.wishlist_purch_buyer_uid=:purch_uid AND p.wishlist_purch_nid=:purch_nid", array(':purch_uid' => $user->uid, ':purch_nid' => $node->nid));
    
    $header = array(
      array('data' => t("Quantity<br>You<br>Purchased")),
      array('data' => t("Action")),
    );

    $rows = array(); // if there were no records, we don't want an undefined variable notification
    foreach($result as $wishlist_purch) {
      if ($wishlist_purch->wishlist_purch_quantity > 1) {
        $action_str = t("Return these items");
      }
      else {
        $action_str = t("Return this item");
      }
      $rows[] = array(
        array("data" => $wishlist_purch->wishlist_purch_quantity),
        array("data" => l($action_str, "wishlist/item/$node->nid/return/$wishlist_purch->wishlist_purch_wid")),
      );

    }
    if ($rows) {
      $output .= "<div class='wishlist_purchased'>" . theme('table', array('header' => $header, 'rows' => $rows)) . "</div>";
    }
  }
  
  return $output;
}



/**
 * Returns the HTML to display a table showing who purchased which gifts for the user, and when.
 * This is part of the node display.  It will only show for users who have 'admin wishlist' or
 * 'reveal purchase status' permissions.
 *
 * @param node $node
 */
function _wishlist_render_purchased_items($node) {
  global $user;

  // Don't show this during a preview.
  if (!$node->nid) {
    return '';
  }

  // To view this region, the user must either have reveal purchase status
  // or be an administrator.
  if (!user_access('admin wishlist') && !user_access('reveal purchase status')) {
    return '';
  }

  // If this is not your entry, then you cannot see the
  // purchase details UNLESS you are an admin.
  if ($user->uid != $node->uid) {
    if (!user_access('admin wishlist')) {
      return '';
    }
  }

  // If the site is configured to hide details from the list owner
  // then check to see if this user, the owner, has the right to reveal
  // and reveal mode is turned on.  If not, do not show this region.
  if (variable_get("wishlist_hide_purchase_info_own", FALSE) && ($user->uid == $node->uid)) {
    if (user_access('reveal purchase status') && isset($_GET['wl_reveal']) && ($_GET['wl_reveal'] != 1)) {
      return '';
    }
  }

//  $sql = "SELECT p.*, u.name FROM {wishlist_purchased} p
//            INNER JOIN {users} u ON u.uid = p.wishlist_purch_buyer_uid
//            WHERE p.wishlist_purch_nid = %d";
//  $result = db_query("SELECT p.*, u.name FROM {wishlist_purchased} p
//            INNER JOIN {users} u ON u.uid = p.wishlist_purch_buyer_uid
//            WHERE p.wishlist_purch_nid = :wishlist_purch_nid", array(':wishlist_purch_nid' => $node->nid));
  $query = db_select('wishlist_purchased', 'p');
  $query->join('users', 'u', 'u.uid = p.wishlist_purch_buyer_uid');
  $query->condition('p.wishlist_purch_nid', $node->nid);
  $query->fields('p');
  $query->addField('u', 'name');
  $result = $query->execute();
  
  $header = array(
    array('data' => t('Purchaser')),
    array('data' => t('On')),
    array('data' => t('Quantity')),
  );
  $rows = array();

  while ($row = $result->fetchObject()) {
    $rows[] = array(
      array('data' => check_plain($row->name)),
      array('data' => ($row->purch_date != 0) ? format_date($row->purch_date, 'custom', 'j M y') : t('unknown')),
      array('data' => $row->wishlist_purch_quantity),
    );
  }

  // If there is no purchaser record, return nothing to display
  if (empty($rows)) {
    return '';
  }

  $output = '<div class="who-purchased">';
  $output .= '<fieldset><legend>' . t('Gift purchase record') . '</legend>' . theme('table', array('header' => $header, 'rows' => $rows)) . '</fieldset>';
  $output .= '</div>';

  return $output;
}





/**
 * Returns a display string based on the currency code passed in.  If the code is not known, the output is set to the input.
 *
 * @param $currency
 *   A three character ISO currency code to translate into display text, if possible
 *
 * @return
 *   A currency code to display on the page
 */
function _wishlist_currency_str($currency) {

  $output = $currency;

  switch (drupal_strtoupper($currency)) {
    case "USD":
      $output = "$";
      break;
    case "HKD":
      $output = "HK$";
      break;
    case "GBP":
      $output = "&pound;";
      break;
    case "JPY":
      $output = "&#165;";
      break;
    case "EUR":
      $output = "&euro;";
      break;
  }

  return $output;
}

/**
 * Gets an associative array (suitable for form_select()) mapping item_priority values to text
 *
 * @return
 *   An associative array relating item_priority values to text strings.
 */
function _wishlist_get_item_priority_array() {
  // Values for the item_priority field
  $options = array(
    "1" => t("I really want this"),
    "2" => t("I'd love to have this"),
    "3" => t("Nice to have"),
    "4" => t("Thinking about it"),
    "5" => t("Don't get this for me"),
  );
  return $options;
}


/**
 * Gets display text for the URL that truncates and removes the http:// protocol header
 *
 * @param $url
 *   The text of the URL string from which to strip away http://
 * @param $maxlength
 *   The maximum length for the return string.  If the string will exceen this length it will be trimmed and ... added to the end
 * @return
 *   A display ready URL fragment
 */
function _wishlist_url($url, $maxlength) {
  $url = str_replace("http://", "", drupal_strtolower(check_plain($url)));
  if (drupal_strlen($url) > $maxlength) {
    $url = truncate_utf8($url, $maxlength -3) . "...";
  }
  return $url;
}

/**
 * Returns the number of items purchased for the node $nid
 *
 * @param $nid
 *   The node to check for purchases against
 * @return
 *   A number - the count of items from that node which have already been purchased.
 */
function _wishlist_get_node_quantity_purchased($nid) {

  // Retrieve the count of the number of this item that have been purchased from the wishlist and
  // return the result
   $query = db_select('wishlist_purchased', 'p');
  $query->addExpression('SUM(wishlist_purch_quantity)', 'quantity_purchased');
  $query->condition('wishlist_purch_nid', $nid);
  $result = $query->execute();
  
  $item_quantity_purchased = 0;
  if ($purchased = $result->fetchObject()) {
    if (!is_NULL($purchased->quantity_purchased)) {
      $item_quantity_purchased = $purchased->quantity_purchased;
    }
  }

  return $item_quantity_purchased;
}


/**
 * Given a URL will check to make sure it contains http or https.
 *
 * @param $url Inbound url to check
 *
 * @return Corrected URL (or the original if no change needed)
 */
function _wishlist_correct_url($url) {
  // check the URL for http and add it if needed
  if ($url) {
    if ((stripos($url, "http://") === FALSE) && (stripos($url, "https://") === FALSE)) {
      $url = 'http://' . $url;
    }
  }

  return $url;
}

/**
 * Utility function to determine whether or not to hide information
 * about the purchase details of a wishlist item.
 *
 * @param $node   The active/current wishlist node
 */
function _wishlist_hide_purchase_info($node) {
  global $user;



  // On private items, we don't hide anything.  Only the user can see these anyway.
  if ($node->item_is_public == FALSE && $user->uid == $node->uid) {
    return FALSE;
  }

  // If this is an anonymous user see if we are configured to hide purchase information
  if (!$user->uid) {
    if (variable_get("wishlist_hide_purchase_info_anonymous", FALSE)) {
      return TRUE;
    }
  }

  // if it is the user's own wishlist entry, and the site is configure to hide purchase details by
  // default, then return TRUE unless the user has chosen to reveal items.
  if ($user->uid == $node->uid) {
    // If this user is authorized to reveal items, then check
    // for the wl_reveal GET parameter.  If it is non-zero, then
    // we won't hide the purchase details.
    if (user_access('reveal purchase status')) {
      if (isset($_GET['wl_reveal']) && ($_GET['wl_reveal'] != 0)) {
        return FALSE;
      }
    }

    if (variable_get("wishlist_hide_purchase_info_own", FALSE)) {
      return TRUE;
    }
  }

  return FALSE;
}


/**
 * Theme function to display the links at the bottom of the node view.
 *
 * @param $url   The URL to format
 * @param $node  The node being displayed
 */
function theme_wishlist_node_url($variables) {
  $url = $variables['url'];
  $node = $variables['node'];
  global $user;

  if ($url == '') {
    return '';
  }

  $output = '<div class="item_url">';
  // If purchase info is hidden, or if all of the items were purchase (if there were any!), do not show
  // the full links to the item.
  if ((($node->item_quantity_requested != 0) && ($node->item_quantity_purchased >= $node->item_quantity_requested))
          || _wishlist_hide_purchase_info($node)) {
    $output .= _wishlist_url($url, 30);
  }
  else {
    if (variable_get("wishlist_url_in_new_window", FALSE)) {
      $url_link_target = "_wishlist_url_window";
    } else {
      $url_link_target = "";
    }

    $output .= t("See") . " " . l(_wishlist_url($url, 50), $url, array('attributes' => array('target' => $url_link_target)));
  }

  if (_wishlist_hide_purchase_info($node)) {
    // If the user is anonymous, then offer a login.  Otherwise we are here
    // because the user is attempting to view their own wishlist
    // and the site does not share status with them.
    if (!$user->uid) {
      $output .= ' - ' . l(t("Login to see full link"), "user/login");
    }
  }
  elseif (($node->item_quantity_purchased >= $node->item_quantity_requested) && $node->item_quantity_requested != 0) {
    $output .= ' - ' . t('None left to purchase');
  }

  $output .= '</div>';

  return $output;
}

/**
 * Returns a form that will allow the user to reveal-all on their lists.  This
 * form will only be shown when a user is viewing their own
 * list or items and when the site is configured to hide the purchased
 * status from them.
 */
function _wishlist_get_reveal_form($wishlist_uid) {
  global $user;

  
  // Only show the form when the list/item is for the current user
  if ($user->uid != $wishlist_uid) {
    return '';
  }

  // If the site is not configured to hide the purchased status, then do
  // not bother with this form.
  if (!variable_get("wishlist_hide_purchase_info_own", FALSE)) {
    return '';
  }

  // And finally if the user is not authorized to reveal purchased
  // status, don't show this form.
  if (!user_access('reveal purchase status')) {
    return '';
  }


  $output = '<div class="reveal-form">';
  $form = drupal_get_form('wishlist_reveal_form');
  $output .= drupal_render($form);
  $output .= '</div>';

  return $output;
}

/*
 * defines the form used to show the 'Show purchase details' menu option
 *
 */
/**
 * Creates a form that allows an authorized user to show who purchased items off their list.
 * @see http://drupal.org/node/1354
 */
function wishlist_reveal_form($form) {
  $form['wishlist_reveal'] = array(
    '#type' => 'select',
    '#name' => 'wishlist_reveal',
    '#title' => '',
    '#default_value' => (isset($_GET['wl_reveal']) ? check_plain($_GET['wl_reveal']) : 0),
    '#options' => array(
      0 => t('Hide purchase details'),
      1 => t('Show purchase details'),
    ),
    '#description' => '',
    '#multiple' => $multiple = FALSE,
    '#required' => $required = FALSE,
    '#attributes' => array('onChange' => "top.location.href='/" . check_plain($_GET['q']) . "?wl_reveal='+this.value"),
  );

  return $form;
}

/**
 * Implements hook_views_tables().
 *
 * Initial views support contributed by draco2002 http://drupal.org/user/76079
 */
function wishlist_views_tables() {
  $tables['wishlist'] = array(
    'name' => 'wishlist',
    'join' => array(
      'left' => array(
        'table' => 'node',
        'field' => 'nid',
      ),
      'right' => array(
        'field' => 'nid',
      ),
    ),
    'fields' => array(
      'item_est_cost' => array(
        'name' => t('Wishlist: Est. Cost'),
        'sortable' => TRUE,
      ),
      'item_url1' => array(
        'name' => t('Wishlist: Item URL 1'),
        'sortable' => TRUE,
      ),
      'item_url2' => array(
        'name' => t('Wishlist: Item URL 2'),
        'sortable' => TRUE,
      ),
      'item_quantity_requested' => array(
        'name' => t('Wishlist: Quantity Requested'),
        'sortable' => TRUE,
      ),
      'item_currency' => array(
        'name' => t('Wishlist: Est. Cost Currency'),
        'sortable' => TRUE,
      ),
      'item_priority' => array(
        'name' => t('Wishlist: Display Priority'),
        'sortable' => TRUE,
      ),
    ),
    'filters' => array(
      'item_is_public' => array(
        'name' => t('Wishlist: Item Is Public'),
        'operator' => array('=' => t('Equals')),
        'list' => 'views_handler_operator_yesno',
        'list-type' => 'select',
        'help' => t('Filter items by public or private status'),
      ),
    ),
    'sorts' => array(
      'item_priority' => array(
        'name' => t('Wishlist: Display Priority'),
        'help' => t('Sort by display priority'),
      ),
    ),
  );

  return $tables;
}

// Views support is included in includes/views/wishlist.views.inc
define('WISHLIST_VIEWS_DIR', drupal_get_path('module', 'wishlist') . '/includes/views');

/**
 * Implements hook_views_api().
 */
function wishlist_views_api() {
  return array(
    'api' => 3,
    'path' => WISHLIST_VIEWS_DIR,
  );
}

/**
 * Implements hook_preprocess_node
 * @param $variables
 *   Array of variables to pass to the node template
 *
 * To use, put a file called node--wishlist.tpl.php in the templates folder of the theme.
 */
function wishlist_preprocess_node(&$variables) {
  if ($variables['type'] == 'wishlist') {
    $node = $variables['node'];
    $variables['wishlist_url1'] = theme('wishlist_node_url', array('url' => $node->item_url1, 'node' => $node));
    $variables['wishlist_url2'] = theme('wishlist_node_url', array('url' => $node->item_url2, 'node' => $node));
    $variables['wishlist_cost'] = '<div class="item_est_cost">' . t("Estimated Cost") . " " . _wishlist_currency_str($variables['item_currency']) . $variables['item_est_cost'] . '</div>';
    $priority_str = _wishlist_get_item_priority_array();
	$variables['wishlist_priority'] = '<div class="priority lessimportant">' . $priority_str[$variables['item_priority']] . '</div>';
    $variables['wishlist_requested'] = '<div class="requested lessimportant">' . t('Requested') . ' ' . $variables['item_quantity_requested'] . '</div>';
    if (_wishlist_hide_purchase_info($node)) {
      $variables['wishlist_purchased'] = '<div class="purchased lessimportant">' . t('Purchase details hidden') . '</div>';
    } else {
      $variables['wishlist_purchased'] = '<div class="purchased lessimportant">' . t('Purchased') . ' ' . $variables['item_quantity_purchased'] . '</div>';
    }
    $variables['wishlist_last_updated'] = "<div class='lastchanged'>" . t("Last updated on") . " " . format_date($variables['changed'], 'short') . "</div>";
    $variables['wishlist_reveal_form'] = _wishlist_get_reveal_form($node->uid);
    $variables['wishlist_items_user_purchased'] = theme('wishlist_items_user_purchased', array('node' => $node));
    $variables['wishlist_purchased_items_table'] = _wishlist_render_purchased_items($node);
  }
}

/**
 * Build user name from tokens if possible, otherwise $user->name
 *
 * @param $user
 *   user object
 * @return
 *   String containing user name built from tokens
 *   If Token module is not enabled or no tokens can be replaced then return $user->name
 */
function _wishlist_user_name_from_tokens($user) {
  if (module_exists('token')) {
    $tokens = variable_get('wishlist_user_name_token', '');
    if (strlen($tokens)) {
	  $name = token_replace($tokens, array('user' => $user));
	  // remove any tokens which have not been replaced (based on http://api.drupal.org/api/drupal/includes--token.inc/function/token_scan/7 )
	  $name = trim(preg_replace('/
        \[             # [ - pattern start
        ([^\s\[\]:]*)  # match $type not containing whitespace : [ or ]
        :              # : - separator
        ([^\s\[\]]*)   # match $name not containing whitespace [ or ]
        \]             # ] - pattern end
        /x', '', $name));
      if (strlen($name)) return $name;
	  }
  }
  return $user->name;
}
