You are here

uc_catalog.module in Ubercart 5

Übercart Catalog module.

Provides classification and navigation product nodes using taxonomy. When installed, this module creates a vocabulary named "Product Catalog" and stores the vocabulary id for future use. The user is responsible for maintaining the terms in the taxonomy, though the Catalog will find products not listed in it.

Coded by Lyle Mantooth

File

uc_catalog/uc_catalog.module
View source
<?php

/**
 * @file
 * Übercart Catalog module.
 *
 * Provides classification and navigation product nodes using taxonomy. When
 * installed, this module creates a vocabulary named "Product Catalog" and stores
 * the vocabulary id for future use. The user is responsible for maintaining the
 * terms in the taxonomy, though the Catalog will find products not listed in it.
 *
 * Coded by Lyle Mantooth
 */

/**
 * Data structure to mimic Drupal's menu system.
 */
class uc_treeNode {
  var $tid = 0;
  var $name = 'Catalog';
  var $children = array();
  var $depth = -1;
  var $sequence = 0;
  function uc_treeNode($term = null) {
    if ($term) {
      $this->tid = $term->tid;
      $this->name = $term->name;
      $this->depth = $term->depth;
      $this->sequence = $term->sequence;
    }
  }

  /**
   * Determines if new child is an immediate descendant or not.
   *
   * This function is completely dependent on the structure of the array returned
   * by taxonomy_get_tree(). Each element in the array knows it's depth in the tree
   * and the array is a preorder iteration of the logical tree structure. Therefore,
   * if the parameter is more than one level deeper than $this, it should be passed
   * to the last child of $this.
   */
  function add_child(&$child) {
    if ($child->depth - $this->depth == 1) {
      $this->children[] = $child;
    }
    else {
      $last_child =& $this->children[count($this->children) - 1];
      $last_child
        ->add_child($child);
    }
  }

}

/******************************************************************************
 * Drupal Hooks                                                               *
 ******************************************************************************/

/**
 * Implementation of hook_menu().
 */
function uc_catalog_menu($may_cache) {
  global $user;
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'catalog',
      'access' => user_access('view catalog'),
      'title' => variable_get('uc_catalog_name', t('Catalog')),
      'callback' => 'theme',
      'callback arguments' => array(
        'uc_catalog_browse',
      ),
      'type' => MENU_SUGGESTED_ITEM,
    );
    $items[] = array(
      'path' => 'admin/store/settings/catalog',
      'access' => user_access('administer catalog'),
      'title' => t('Catalog settings'),
      'description' => t('Configure the catalog settings.'),
      'callback' => 'uc_catalog_settings_overview',
      'type' => MENU_NORMAL_ITEM,
    );
    $items[] = array(
      'path' => 'admin/store/settings/catalog/overview',
      'access' => user_access('administer catalog'),
      'title' => t('Overview'),
      'weight' => -10,
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/store/settings/catalog/edit',
      'access' => user_access('administer catalog'),
      'title' => t('Edit'),
      'callback' => 'uc_catalog_admin_settings',
      'weight' => -5,
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/store/settings/catalog/edit/catalog',
      'access' => user_access('administer catalog'),
      'title' => t('Catalog'),
      'weight' => -10,
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/store/settings/catalog/edit/grid',
      'access' => user_access('administer catalog'),
      'title' => t('Grid'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'uc_catalog_grid_admin_form',
      ),
      'weight' => -5,
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/store/products/orphans',
      'title' => t('Find orphaned products'),
      'access' => user_access('administer catalog'),
      'callback' => 'uc_catalog_orphaned_products',
      'description' => t('Find products that have not been categorized.'),
      'type' => MENU_NORMAL_ITEM,
      'weight' => -4,
    );
    if (module_exists('ubrowser')) {
      $items[] = array(
        'path' => 'admin/store/products/categories',
        'access' => user_access('administer catalog'),
        'title' => t('Move products'),
        'callback' => 'drupal_get_form',
        'callback arguments' => 'uc_catalog_set_category_form',
        'type' => MENU_NORMAL_ITEM,
        'weight' => -6,
      );
    }
  }
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function uc_catalog_perm() {
  return array(
    'view catalog',
    'administer catalog',
  );
}

/**
 * Implementation of hook_enable().
 *
 * Add imagecache preset "uc_category".
 */
function uc_catalog_enable() {
  if (module_exists('imagecache')) {
    $result = db_query("SELECT presetid FROM {imagecache_preset} WHERE presetname = 'uc_category'");
    if (!db_fetch_object($result)) {
      $id = db_next_id('{imagecache_preset}_presetid');
      db_query("INSERT INTO {imagecache_preset} (presetid, presetname) VALUES (%d, 'uc_category')", $id);
      db_query("INSERT INTO {imagecache_action} (actionid, presetid, weight, data) VALUES (%d, %d, 0, '%s')", db_next_id('{imagecache_action}_actionid'), $id, 'a:4:{s:8:"function";s:5:"scale";s:3:"fit";s:6:"inside";s:5:"width";s:2:"96";s:6:"height";s:2:"96";}');
      cache_clear_all('imagecache:presets', 'cache');
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function uc_catalog_nodeapi(&$node, $op, $a3 = null, $a4 = null) {
  static $parents = array();
  if (in_array($node->type, module_invoke_all('product_types'))) {
    switch ($op) {
      case 'view':
        if ($a4 == true && variable_get('uc_catalog_breadcrumb', true)) {
          $crumbs = array();
          if (variable_get('site_frontpage', 'node') != 'catalog') {
            $crumbs[] = l(t('Home'), '');
          }
          $terms = taxonomy_node_get_terms_by_vocabulary($node->nid, variable_get('uc_catalog_vid', 0));
          if (count($terms)) {
            $crumbs[] = l(variable_get('uc_catalog_name', t('Catalog')), variable_get('uc_catalog_url', 'catalog'));
            $used_tids = array();
            foreach ($terms as $term) {
              if (!isset($parents[$term->tid])) {
                $parents[$term->tid] = taxonomy_get_parents_all($term->tid);
              }

              //drupal_set_message('<pre>'. print_r($parents[$term->tid], true) .'</pre>');
              foreach (array_reverse($parents[$term->tid]) as $parent) {
                if (!in_array($parent->tid, $used_tids)) {
                  $crumbs[] = l($parent->name, uc_catalog_path($parent));
                  $used_tids[] = $parent->tid;
                }
              }
            }
          }
          drupal_set_breadcrumb($crumbs);
        }
        break;
    }
  }
}

/**
 * Implementation of hook_taxonomy().
 */
function uc_catalog_taxonomy($op, $type, $object = null) {
  switch ($type) {
    case 'vocabulary':
      if ($object['vid'] == variable_get('uc_catalog_vid', 0)) {
        switch ($op) {
          case 'delete':
            variable_del('uc_catalog_vid');
            variable_del('uc_catalog_name');
            variable_del('uc_catalog_description');
            break;
          case 'update':
            variable_set('uc_catalog_name', check_plain($object['name']));
            variable_set('uc_catalog_description', filter_xss_admin($object['description']));
            break;
        }
      }
      break;
    case 'term':
      switch ($op) {
        case 'insert':
          if (module_exists('pathauto')) {
            $category = (object) $object;
            if ($category->name) {
              $count = _uc_catalog_pathauto_alias($category, $op);
            }
          }
          $field_name = 'image';
          if ($file = file_check_upload($field_name)) {
            $file->filepath = str_replace('\\', '/', $file->filepath);
            $image_path = file_create_path();
            $file = file_save_upload($field_name, $image_path . '/' . $file->filename);
            if ($file) {
              if (image_get_info($file->filepath)) {
                db_query("INSERT INTO {uc_catalog_images} (fid, tid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", db_next_id('{files}_fid'), $object['tid'], $file->filename, $file->filepath, $file->filemime, $file->filesize);
              }
              else {
                form_set_error($field_name, t('Uploaded file is not a valid image'));
                file_delete($file->filepath);
              }
            }
          }
          break;
        case 'update':
          if (module_exists('pathauto')) {
            $category = (object) $object;
            if ($category->name) {
              $count = _uc_catalog_pathauto_alias($category, $op);
            }
          }
          $field_name = 'image';
          if ($object['remove']) {
            db_query("DELETE FROM {uc_catalog_images} WHERE tid = %d", $object['tid']);
          }
          else {
            if ($file = file_check_upload($field_name)) {
              $file->filepath = str_replace('\\', '/', $file->filepath);
              $file = file_save_upload($field_name, file_create_path() . '/' . $file->filename);
              if ($file) {
                if (image_get_info($file->filepath)) {
                  db_query("DELETE FROM {uc_catalog_images} WHERE tid = %d", $object['tid']);
                  db_query("INSERT INTO {uc_catalog_images} (fid, tid, filename, filepath, filemime, filesize) VALUES (%d, %d, '%s', '%s', '%s', %d)", db_next_id('{files}_fid'), $object['tid'], $file->filename, $file->filepath, $file->filemime, $file->filesize);
                }
                else {
                  form_set_error($field_name, t('Uploaded file is not a valid image'));
                  file_delete($file->filepath);
                }
              }
            }
          }
          break;
        case 'delete':
          $category = (object) $object;
          if ($file = db_fetch_object(db_query("SELECT fid, filepath FROM {uc_catalog_images} WHERE tid = %d", $category->tid))) {
            file_delete($file->filepath);
            db_query("DELETE FROM {uc_catalog_images} WHERE fid = %d", $file->fid);
          }
          path_set_alias(uc_catalog_path($category));
          break;
      }
      break;
  }
}

/**
 * Add an image field to the catalog's taxonomy term form.
 */
function uc_catalog_form_alter($form_id, &$form) {
  if ($form_id == 'taxonomy_form_term' && $form['vid']['#value'] == variable_get('uc_catalog_vid', 0)) {
    $form['#attributes'] = array(
      "enctype" => "multipart/form-data",
    );
    $form['name']['#weight'] = -1;
    $form['image']['#weight'] = 0;
    $form['image']['image'] = array(
      '#type' => 'file',
      '#title' => t('Image'),
      '#weight' => 0,
    );
    $image = uc_catalog_image_load($form['tid']['#value']);
    if ($image) {
      if (module_exists('imagecache')) {
        $image_display = theme('imagecache', 'uc_category', $image->filepath);
      }
      else {
        $image_display = theme('image', $image->filename, t('Term image'));
        $form['image']['image']['#description'] = t('The image will not be resized. Consider installing <a href="@url">Image cache</a>.', array(
          '@url' => url('http://drupal.org/project/imagecache'),
        ));
      }
      $form['image']['remove'] = array(
        '#type' => 'checkbox',
        '#title' => t('Remove category image: !image', array(
          '!image' => $image_display,
        )),
        '#weight' => 1,
      );
    }
    $form['description']['#description'] = t('A description of the term. Displayed to customers at the top of catalog pages.');
  }
}

/**
 * Implementation of hook_link_alter().
 *
 * Rewrite taxonomy term links to point to the catalog.
 */
function uc_catalog_link_alter(&$node, &$links) {

  // Link back to the catalog and not the taxonomy term page
  foreach ($links as $module => $link) {
    if (strstr($module, 'taxonomy_term')) {
      $tid = explode('_', $module);
      $tid = $tid[2];
      $term = taxonomy_get_term($tid);
      if ($term->vid == variable_get('uc_catalog_vid', 0)) {
        $links[$module]['href'] = uc_catalog_path($term);
      }
    }
  }
}

/**
 * Displays a menu for navigating the "Product Catalog"
 */
function uc_catalog_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks[0] = array(
        'info' => variable_get('uc_catalog_name', t('Catalog')),
        'status' => 1,
      );
      return $blocks;
    case 'view':
      $block = array();
      if (user_access('view catalog')) {
        switch ($delta) {
          case 0:

            // Get the vocabulary tree information.
            $vid = variable_get('uc_catalog_vid', 0);
            $tree = taxonomy_get_tree($vid);

            // Then convert it into an actual tree structure.
            $seq = 0;
            $menu_tree = new uc_treeNode();
            $level = array();
            $curr_depth = -1;
            foreach ($tree as $knot) {
              $seq++;
              $knot->sequence = $seq;
              $knothole = new uc_treeNode($knot);

              // Begin at the root of the tree and find the proper place.
              $menu_tree
                ->add_child($knothole);
            }

            // Now, create a structured menu, separate from Drupal's menu.
            $content .= theme("uc_catalog_block_start");
            foreach ($menu_tree->children as $branch) {
              list($inpath, $html) = _uc_catalog_navigation($branch);
              $content .= $html;
            }
            $content .= theme("uc_catalog_block_end");
            $subject = variable_get('uc_catalog_name', t('Catalog'));
            if (variable_get('uc_catalog_block_title', true)) {
              $subject = l($subject, 'catalog');
            }
            $block = array(
              'subject' => $subject,
              'content' => $content,
            );
            break;
        }
      }
      return $block;
  }
}

/**
 * Theme the beginning of the catalog block.
 *
 * @ingroup themeable
 * @see theme_uc_catalog_item
 * @see theme_uc_catalog_block_end
 */
function theme_uc_catalog_block_start() {
  return '<ul class="menu">';
}

/**
 * Theme the end of the catalog block.
 *
 * @ingroup themeable
 * @see theme_uc_catalog_block_start
 * @see theme_uc_catalog_item
 */
function theme_uc_catalog_block_end() {
  return '</ul>';
}

/******************************************************************************
 * Module Hooks                                                             *
 ******************************************************************************/
function uc_catalog_pathauto($op) {
  switch ($op) {
    case 'settings':
      $settings = array();
      $settings['module'] = 'uc_catalog';
      $settings['token_type'] = 'taxonomy';
      $settings['groupheader'] = t('Catalog path settings');
      $settings['patterndescr'] = t('Pattern for catalog pages');
      $settings['patterndefault'] = t('catalog/[catpath-raw]');
      $patterns = token_get_list('taxonomy');
      foreach ($patterns as $type => $pattern_set) {
        if ($type != 'global') {
          foreach ($pattern_set as $pattern => $description) {
            $settings['placeholders']['[' . $pattern . ']'] = $description;
          }
        }
      }
      $settings['supportsfeeds'] = '0/feed';
      $settings['bulkname'] = t('Bulk generate aliases for catalog pages that are not aliased');
      $settings['bulkdescr'] = t('Generate aliases for all existing catalog pages which do not already have aliases. Note: Bulk Update may not complete on large or slow sites. See the README.txt for more information.');
      return (object) $settings;
  }
}

/**
 * Generate aliases for all categories without aliases
 *
 */
function uc_catalog_pathauto_bulkupdate() {
  $catalog_vid = variable_get('uc_catalog_vid', 0);
  $query = "SELECT tid, vid, name, src, dst FROM {term_data} LEFT JOIN {url_alias} ON src LIKE CONCAT('catalog/', CAST(tid AS CHAR)) WHERE src IS NULL AND vid = %d";
  $result = db_query_range($query, $catalog_vid, 0, variable_get('pathauto_max_bulk_update', 50));
  $count = 0;
  $placeholders = array();
  while ($category = db_fetch_object($result)) {
    $count = _uc_catalog_pathauto_alias($category, 'bulkupdate') + $count;
  }
  drupal_set_message(format_plural($count, "Bulk generation of terms completed, @count alias generated.", "Bulk generation of terms completed, @count aliases generated."));
}

/**
 *
 * Function to create aliases for taxonomy objects
 *
 * @param object $category a taxonomy object
 *
 */
function _uc_catalog_pathauto_alias($category, $op) {
  _pathauto_include();
  $count = 0;
  $placeholders = pathauto_get_placeholders('taxonomy', $category);
  $src = uc_catalog_path($category);
  if ($alias = pathauto_create_alias('uc_catalog', $op, $placeholders, $src, $category->vid)) {
    $count++;
  }
  return $count;
}
function uc_catalog_path_alias_types() {
  return array(
    'catalog/' => t('catalog pages'),
  );
}

/******************************************************************************
 * Übercart Hooks                                                             *
 ******************************************************************************/

/**
 * Implementation of Übercart's hook_store_status().
 *
 * Provide status information about the "Product Catalog" and products not listed in the catalog.
 */
function uc_catalog_store_status() {
  $statuses = array();
  $cat_id = variable_get('uc_catalog_vid', 0);
  $catalog = taxonomy_get_vocabulary($cat_id);
  if ($catalog) {
    $statuses[] = array(
      'status' => 'ok',
      'title' => t('Catalog vocabulary'),
      'desc' => t('Vocabulary !name has been identified as the !uber catalog.', array(
        '!name' => l($catalog->name, 'admin/content/taxonomy/' . $catalog->vid),
        '!uber' => '&Uuml;bercart',
      )),
    );
    $excluded = 0;
    $result = db_query("SELECT DISTINCT * FROM {node} AS n LEFT JOIN {term_node} AS tn ON n.nid = tn.nid LEFT JOIN {vocabulary_node_types} AS vnt ON n.type = vnt.type WHERE n.type != 'image' AND tn.tid IS NULL AND vnt.vid = %d", $cat_id);
    $excluded = db_num_rows($result);
    if ($excluded) {
      $description = format_plural($excluded, 'There is @count product not listed in the catalog.', 'There are @count products not listed in the catalog.') . t(' Products are listed by assigning a category from the <a href="!cat_url">Product Catalog</a> vocabulary to them.', array(
        '!cat_url' => url('admin/content/taxonomy/' . $catalog->vid),
      ));
      $terms = db_result(db_query("SELECT COUNT(*) FROM {term_data} WHERE vid = %d", $catalog->vid));
      if ($terms) {
        $description .= ' ' . l(t('Find orphaned products here.'), 'admin/store/products/orphans');
      }
      else {
        $description .= ' ' . l(t('Add terms for the products to inhabit.'), 'admin/content/taxonomy/' . $catalog->vid . '/add/term');
      }
      $statuses[] = array(
        'status' => 'warning',
        'title' => t('Unlisted products'),
        'desc' => $description,
      );
    }
  }
  else {
    $statuses[] = array(
      'status' => 'error',
      'title' => t('Catalog vocabulary'),
      'desc' => t('No vocabulary has been recognized as the Ubercart catalog. Choose one on <a href="!admin_catalog">this page</a> or click <a href="!admin_vocab">here</a> to create one first.', array(
        '!admin_catalog' => url('admin/store/settings/catalog'),
        '!admin_vocab' => url('admin/content/taxonomy'),
      )),
    );
  }
  return $statuses;
}

/**
 * Implementation of Übercart's hook_product_class.
 *
 * Add and remove product node types to the catalog vocabulary as they are
 *   created and deleted.
 */
function uc_catalog_product_class($type, $op) {
  $vid = variable_get('uc_catalog_vid', 0);
  switch ($op) {
    case 'insert':
      if ($vid) {
        db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $vid, $type);
      }
      break;
    case 'delete':
      if ($vid) {
        db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d AND type = '%s'", $vid, $type);
      }
      break;
  }
}

/******************************************************************************
 * Menu Callbacks                                                             *
 ******************************************************************************/

/**
 * Display an overview of all catalog settings.
 */
function uc_catalog_settings_overview() {
  $sections = array();
  $sections[] = array(
    'edit' => 'admin/store/settings/catalog/edit',
    'title' => t('Catalog settings'),
    'items' => array(
      t('The catalog vocabulary id is !vid.', array(
        '!vid' => variable_get('uc_catalog_vid', 0),
      )),
      t('The base URL pointing to the catalog is !url.', array(
        '!url' => 'catalog',
      )),
      variable_get('uc_catalog_breadcrumb', true) ? t('The catalog breadcrumb will be shown.') : t('The catalog breadcrumb will not be shown.'),
      variable_get('uc_catalog_breadcrumb_nodecount', false) ? t('The number of nodes in a category will be shown in the catalog breadcrumb.') : t('The number of nodes in a category will not be shown in the catalog breadcrumb.'),
      format_plural(variable_get('uc_catalog_category_columns', 3), 'Subcategories will be displayed in @count column.', 'Subcategories will be displayed in @count columns.'),
    ),
  );
  $sections[] = array(
    'edit' => 'admin/store/settings/catalog/edit/grid',
    'title' => t('Products grid settings'),
    'items' => array(
      variable_get('uc_catalog_grid_display', false) ? t('The catalog will be displayed on a grid.') : t('The catalog will be displayed on a table list.'),
      format_plural(variable_get('uc_catalog_grid_display_width', 3), 'The grid will be displayed in @count column.', 'The grid will be displayed in @count columns.'),
      variable_get('uc_catalog_grid_display_title', true) ? t('Every cell on the grid will display the Product Title.') : t('Cells on the grid will not display Product Titles.'),
      variable_get('uc_catalog_grid_display_model', true) ? t('Every cell on the grid will display the Product SKU.') : t('Cells on the grid will not display Product SKU.'),
      variable_get('uc_catalog_grid_display_sell_price', true) ? t('Every cell on the grid will display the Product Selling Price.') : t('Cells on the grid will not display Product Selling Prices.'),
      variable_get('uc_catalog_grid_display_add_to_cart', true) ? t('Every cell on the grid will display the Add to Cart button.') : t('Cells on the grid will not display Add to Cart buttons.'),
      variable_get('uc_catalog_grid_display_attributes', true) ? t('Every cell on the grid will display Product Attibutes.') : t('Cells on the grid will not display Product Attibutes.'),
    ),
  );
  $sections[] = array(
    'edit' => 'admin/store/settings/catalog/edit/blocks',
    'title' => t('Catalog block settings'),
    'items' => array(
      variable_get('uc_catalog_block_nodecount', true) ? t('The number of nodes in a category will be shown in the catalog block.') : t('The number of nodes in a category will not be shown in the catalog block.'),
      variable_get('uc_catalog_show_subcategories', true) ? t('Subcategories with no products to display will be shown on category pages.') : t('Subcategories with no products to display will not be shown on category pages.'),
      variable_get('uc_catalog_expand_categories', false) ? t('Categories in the catalog block will always be expanded.') : t('Categories in the catalog block will be expanded only when selected.'),
    ),
  );
  $output = theme('uc_settings_overview', $sections);
  return $output;
}

/**
 * Determines if the "Product Catalog" vocabulary has been set up.
 *
 * @see uc_catalog_admin_form
 */
function uc_catalog_admin_settings() {
  $output = '';
  $vid = variable_get('uc_catalog_vid', null);
  if ($vid) {
    $catalog = taxonomy_get_vocabulary($vid);
    $output .= t('Vocabulary %name is set as the product catalog. ', array(
      '%name' => $catalog->name,
    ));

    //$output .= l(t('Build a term hierarchy for products there.'), 'admin/content/taxonomy/'. $vid .'/add/term');

    //drupal_set_message($output);
    $output .= l(t('View the catalog here.'), 'admin/content/taxonomy/' . $vid);
  }
  $output .= drupal_get_form('uc_catalog_admin_form');
  return $output;
}

/**
 * Determines if the "Product Catalog" vocabulary has been set up.
 *
 * @see uc_catalog_admin_form
 */
function uc_catalog_grid_admin_settings() {
  $output = '';
  $output .= drupal_get_form('uc_catalog_grid_admin_form');
  return $output;
}

/**
 * Determines if the "Product Catalog" vocabulary has been set up.
 *
 * @see uc_catalog_admin_form
 */
function uc_catalog_blocks_admin_settings() {
  $output = '';
  $output .= drupal_get_form('uc_catalog_blocks_admin_form');
  return $output;
}

/**
 * Form to change the category of many nodes at once.
 *
 * @ingroup forms
 * @see uc_catalog_set_category_form_submit
 */
function uc_catalog_set_category_form() {
  drupal_add_css(drupal_get_path('module', 'uc_catalog') . '/uc_catalog.css');
  drupal_add_css(drupal_get_path('module', 'uc_product') . '/uc_product.css');
  $settings = array(
    'div' => '#category-selector',
    'vid' => variable_get('uc_catalog_vid', 0),
    'filter' => implode(',', module_invoke_all('product_types')),
    'search' => 'true',
    'nids' => 'true',
    'nodesg' => 'product',
    'nodepl' => 'products',
    'multi' => 'true',
    'close' => 'false',
    'select' => 'buffer_products("' . file_create_url('') . '")',
  );
  $form['selector'] = array(
    '#type' => 'markup',
    '#value' => ubrowser($settings, 'category-selector'),
  );
  $form['buffer'] = uc_product_buffer_form(func_get_args());
  $form['buffer']['#prefix'] = '<div class="product-buffer">';
  $form['buffer']['#suffix'] = '</div>';
  unset($form['buffer']['submit']);
  uc_add_js(drupal_get_path('module', 'uc_product') . '/uc_product.js', 'module');
  $form['categories'] = taxonomy_form(variable_get('uc_catalog_vid', 0));
  $form['categories']['#title'] = t('New category');
  unset($form['categories']['#weight']);
  $form['move'] = array(
    '#type' => 'submit',
    '#value' => t('Move'),
  );
  $form['copy'] = array(
    '#type' => 'submit',
    '#value' => t('Copy'),
  );
  return $form;
}

/**
 * Submit function for uc_catalog_set_category_form().
 */
function uc_catalog_set_category_form_submit($form_id, $form_values) {
  $categories = (array) $form_values['categories'];
  $catalog_vid = variable_get('uc_catalog_vid', 0);
  foreach (array_filter(explode('/', $form_values['products'])) as $nid) {

    // Preserve terms already associated with this product.
    $terms = taxonomy_node_get_terms($nid);
    foreach ($terms as $term) {

      // Don't preserve those from the catalog if products are being moved.
      if ($form_values['op'] == t('Move') && $term->vid == $catalog_vid) {
        continue;
      }
      $categories[] = $term->tid;
    }
    taxonomy_node_save($nid, array_unique($categories));
  }
}

/**
 * Load catalog information for display.
 *
 * Retrieve image, product, and subcategory information for the current term id.
 *
 * @param $tid
 *   Taxonomy term id.
 * @return
 *   A catalog object containing all the information needed to display a catalog page.
 */
function uc_catalog_get_page($tid) {
  $catalog = new stdClass();
  $vid = variable_get('uc_catalog_vid', 0);
  if ($tid) {
    $term = taxonomy_get_term($tid);
    $name = $term->name;
    $description = $term->description;
  }
  else {
    $tid = 0;
    $name = variable_get('uc_catalog_name', t('Catalog'));
    $description = variable_get('uc_catalog_description', '');
  }
  $catalog->tid = $tid;
  $catalog->vid = $vid;
  $catalog->name = $name;
  $catalog->description = $description;
  $catalog->children = array();
  if ($file = uc_catalog_image_load($catalog->tid)) {
    if (module_exists('imagecache')) {
      $file_path = file_create_url(file_directory_path() . '/imagecache/uc_category/' . $file->filepath);
    }
    else {
      $file_path = $file->filepath;
    }
    $info = image_get_info($file_path);
    $catalog->image = $info;
    $catalog->image['filepath'] = $file->filepath;
  }
  $types = module_invoke_all('product_types');
  $links = array();
  $child_list = array();
  $children = taxonomy_get_children($tid, $vid);
  foreach ($children as $child) {
    $n = 0;
    foreach ($types as $type) {
      $n += taxonomy_term_count_nodes($child->tid, $type);
    }
    $child->nodes = $n;

    // Display child category's image.
    if (module_exists('imagecache') && ($file = uc_catalog_image_load($child->tid))) {
      $imagecache_path = file_create_url(file_directory_path() . '/imagecache/uc_category/' . $file->filepath);
      $info = image_get_info($imagecache_path);
      $child->image = $info;
      $child->image['filepath'] = $file->filepath;
    }

    // Display list of child category's children categories.
    // If more than $max_gc_display, show "More..." link to child.
    $grandchildren_list = taxonomy_get_children($child->tid, $vid);
    $child->children = $grandchildren_list;
    $catalog->children[] = $child;
  }

  //$node_resource = taxonomy_select_nodes(array($tid));
  return $catalog;
}

/**
 * Display a formatted catalog page.
 *
 * If the category has products in it, display them in a TAPIr table. Subcategories
 *   are linked along the top of the page. If it does not have products, display
 *   subcategories in a grid with their images and subcategories.
 *
 * @param $tid
 *   Catalog term id from URL.
 * @return
 *   Formatted HTML of the catalog page.
 */
function theme_uc_catalog_browse($tid = 0) {
  drupal_add_css(drupal_get_path('module', 'uc_catalog') . '/uc_catalog.css');
  $output = '';
  $catalog = uc_catalog_get_page((int) $tid);
  drupal_set_title(check_plain($catalog->name));
  drupal_set_breadcrumb(uc_catalog_set_breadcrumb($catalog->tid));
  $types = module_invoke_all('product_types');
  $links = array();
  $child_list = array();
  foreach ($catalog->children as $child) {
    if ($child->nodes) {
      $links[] = array(
        'title' => $child->name . (variable_get('uc_catalog_breadcrumb_nodecount', false) ? ' (' . $child->nodes . ')' : ''),
        'href' => uc_catalog_path($child),
        'attributes' => array(
          'rel' => 'tag',
        ),
      );
    }
    if ($child->image) {
      $image = '<div>';
      if (module_exists('imagecache')) {
        $image .= l(theme('imagecache', 'uc_category', $child->image['filepath']), uc_catalog_path($child), array(), null, null, false, true);
      }
      else {
        $image .= l(theme('image', $child->image['filepath']), uc_catalog_path($child), array(), null, null, false, true);
      }
      $image .= '</div>';
    }
    else {
      $image = '<div></div>';
    }
    $grandchildren = array();
    $j = 0;
    $max_gc_display = 3;
    foreach ($child->children as $i => $grandchild) {
      if ($j > $max_gc_display) {
        break;
      }
      $g_child_nodes = 0;
      foreach ($types as $type) {
        $g_child_nodes += taxonomy_term_count_nodes($grandchild->tid, $type);
      }
      if ($g_child_nodes) {
        $grandchildren[$i] = l($grandchild->name, uc_catalog_path($grandchild), array(
          'class' => 'subcategory',
        ));
        $j++;
      }
    }

    //$grandchildren = array_slice($grandchildren, 0, intval(count($grandchildren) / 2) + 1, true);
    if ($j > $max_gc_display) {
      array_push($grandchildren, l(t('More...'), uc_catalog_path($child), array(
        'class' => 'subcategory',
      )));
    }
    if ($child->nodes) {
      $cell_link = $image . '<strong>' . l($child->name, uc_catalog_path($child)) . '</strong>';
      if (variable_get('uc_catalog_show_subcategories', true)) {
        $cell_link .= "<br /><span>" . implode(', ', $grandchildren) . "</span>\n";
      }
      $child_list[] = $cell_link;
    }
  }
  if ($catalog->image) {
    $output .= theme('imagecache', 'uc_thumbnail', $catalog->image['filepath'], $catalog->name, $catalog->name, array(
      'class' => 'category',
    ));
  }
  $header = tapir_get_header('uc_product_table', array());
  $order = substr(tablesort_sql($header), 10);
  if (empty($_REQUEST['order'])) {
    $order = 'p.ordering, n.title, n.nid';
  }
  $product_types = module_invoke_all('product_types');
  $sql = "SELECT DISTINCT(n.nid), n.sticky, n.title, n.created, p.model, p.sell_price, p.ordering\n    FROM {node} n\n      INNER JOIN {term_node} tn ON n.nid = tn.nid\n      INNER JOIN {uc_products} AS p ON n.vid = p.vid\n    WHERE tn.tid = %d AND n.status = 1\n      AND n.type IN ('" . implode("','", $product_types) . "')\n    ORDER BY " . $order;
  $sql_count = 'SELECT COUNT(DISTINCT(n.nid))
    FROM {node} n
      INNER JOIN {term_node} tn ON n.nid = tn.nid
      INNER JOIN {uc_products} AS p ON n.nid = p.nid
    WHERE tn.tid = %d
      AND n.status = 1
      AND n.type IN ("' . implode('","', $product_types) . '")';
  $sql = db_rewrite_sql($sql);
  $sql_count = db_rewrite_sql($sql_count);
  $catalog->products = array();
  $result = pager_query($sql, variable_get('uc_product_nodes_per_page', 12), 0, $sql_count, $catalog->tid);
  while ($node = db_fetch_object($result)) {
    $catalog->products[] = $node->nid;
  }
  if (count($catalog->products)) {
    if (count($links)) {
      $output .= theme('links', $links, array(
        'class' => 'links inline',
      )) . "<br />\n";
    }
    $output .= $catalog->description;
    $output .= theme('uc_catalog_products', $catalog->products);
    $output .= theme('pager');
  }
  else {

    // Display table of child categories similar to an osCommerce site's front page.
    $columns = variable_get('uc_catalog_category_columns', 3);
    $cat_rows = array();
    $row = array();
    $i = 1;
    foreach ($child_list as $cell) {
      $row[] = array(
        'data' => $cell,
        'class' => 'category',
      );
      if ($i % $columns == 0) {
        $cat_rows[] = $row;
        $row = array();
      }
      $i++;
    }
    if (count($row) > 0 && count($row) < $columns) {
      if (count($cat_rows) >= 1) {
        $row = array_merge($row, array_fill(count($row), $columns - count($row), array(
          'data' => '&nbsp;',
          'class' => 'category',
        )));
      }
      $cat_rows[] = $row;
    }
    $output .= $catalog->description;
    $output .= theme('table', array(), $cat_rows, array(
      'class' => 'category',
    ));
  }
  return $output;
}

/**
 * Display a formatted list of products.
 *
 * @param $products
 *   An array of product nids.
 * @return
 *   A TAPIr table.
 * @ingroup themeable
 */
function theme_uc_catalog_products($products) {
  if (!$products) {
    $output .= '<div class="no-products">' . t('No products are available in this category.') . '</div>';
    return $output;
  }
  else {
    if (variable_get('uc_catalog_grid_display', false)) {
      return theme('uc_catalog_product_grid', $products);
    }
    else {
      $table_args = array(
        'nids' => $products,
        'attributes' => array(
          'class' => 'category-products',
        ),
      );
      return tapir_get_table('uc_product_table', $table_args);
    }
  }
}
function theme_uc_catalog_product_grid($products) {
  $product_table = '<div class="category-grid-products"><table>';
  $count = 0;
  foreach ($products as $nid) {
    $product = node_load($nid);
    if ($count == 0) {
      $product_table .= "<tr>";
    }
    else {
      if ($count % variable_get('uc_catalog_grid_display_width', 3) == 0) {
        $product_table .= "</tr><tr>";
      }
    }
    $titlelink = l($product->title, "node/{$nid}", array(), null, null, false, true);
    if (module_exists('imagecache') && isset($product->field_image_cache) && file_exists($product->field_image_cache[0]['filepath'])) {
      $imagelink = l(theme('imagecache', 'product_list', $product->field_image_cache[0]['filepath'], $product->title, $product->title), "node/{$nid}", array(), null, null, false, true);
    }
    else {
      $imagelink = '';
    }
    $product_table .= '<td>';
    if (variable_get('uc_catalog_grid_display_title', TRUE)) {
      $product_table .= '<span class="catalog_grid_title">' . $titlelink . '</span>';
    }
    if (variable_get('uc_catalog_grid_display_model', TRUE)) {
      $product_table .= '<span class="catalog_grid_ref">' . $product->model . '</span>';
    }
    $product_table .= '<span class="catalog_grid_image">' . $imagelink . '</span>';
    if (variable_get('uc_catalog_grid_display_sell_price', TRUE)) {
      $product_table .= '<span class="catalog_grid_sell_price">' . uc_currency_format($product->sell_price) . '</span>';
    }
    if (variable_get('uc_catalog_grid_display_add_to_cart', TRUE)) {
      if (variable_get('uc_catalog_grid_display_attributes', TRUE)) {
        $product_table .= theme('uc_product_add_to_cart', $product);
      }
      else {
        $product_table .= drupal_get_form('uc_catalog_buy_it_now_form_' . $product->nid, $product);
      }
    }
    $product_table .= '</td>';
    $count++;
  }
  $product_table .= "</tr></table></div>";
  return $product_table;
}

/******************************************************************************
 * Module Functions                                                           *
 ******************************************************************************/

/**
 * Catalog settings form.
 *
 * Configure the display of the catalog breadcrumb.
 *
 * @ingroup forms
 */
function uc_catalog_admin_form() {
  $form = array();
  $vocabs = array();
  $vocabularies = taxonomy_get_vocabularies();
  foreach ($vocabularies as $vid => $vocabulary) {
    $vocabs[$vid] = $vocabulary->name;
  }

  // JTR - catalog-top-level sub-textfield
  $form['catalog-top-level'] = array(
    '#type' => 'fieldset',
    '#title' => t('Catalog top level'),
    '#collapsible' => true,
    '#collapsed' => false,
    '#attributes' => array(
      'class' => 'catalog-top-level',
    ),
  );
  $form['catalog-top-level']['uc_catalog_vid'] = array(
    '#type' => 'select',
    '#title' => t('Catalog vocabulary'),
    '#description' => t("The taxonomy vocabulary that will be considered the product catalog."),
    '#default_value' => variable_get('uc_catalog_vid', 0),
    '#options' => $vocabs,
  );
  $form['catalog-top-level']['uc_catalog_breadcrumb'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display the catalog breadcrumb'),
    '#default_value' => variable_get('uc_catalog_breadcrumb', true),
  );
  $form['catalog-top-level']['uc_catalog_breadcrumb_nodecount'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display node counts in the catalog breadcrumb'),
    '#default_value' => variable_get('uc_catalog_breadcrumb_nodecount', false),
  );
  $form['catalog-top-level']['uc_catalog_show_subcategories'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display subcategories in the catalog view'),
    '#default_value' => variable_get('uc_catalog_show_subcategories', true),
  );
  $form['catalog-top-level']['uc_catalog_category_columns'] = array(
    '#type' => 'select',
    '#title' => t('Number of columns in the grid of categories'),
    '#default_value' => variable_get('uc_catalog_category_columns', 3),
    '#options' => drupal_map_assoc(uc_range(1, 5)),
  );
  $form['block-display'] = array(
    '#type' => 'fieldset',
    '#title' => t('Catalog block settings'),
    '#collapsible' => true,
    '#collapsed' => false,
    '#attributes' => array(
      'class' => 'block-display',
    ),
  );
  $form['block-display']['uc_catalog_block_title'] = array(
    '#type' => 'checkbox',
    '#title' => t('Block title links to top level catalog page'),
    '#default_value' => variable_get('uc_catalog_block_title', true),
    '#description' => t("If selected, the block title is a link, otherwise plain text."),
  );
  $form['block-display']['uc_catalog_expand_categories'] = array(
    '#type' => 'checkbox',
    '#title' => t('Always expand categories in the catalog block'),
    '#default_value' => variable_get('uc_catalog_expand_categories', false),
  );
  $form['block-display']['uc_catalog_block_nodecount'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display node counts in the catalog block'),
    '#default_value' => variable_get('uc_catalog_block_nodecount', true),
  );
  $form['catalog-products-list'] = array(
    '#type' => 'fieldset',
    '#title' => t('Catalog products list'),
    '#collapsible' => true,
    '#collapsed' => false,
    '#attributes' => array(
      'class' => 'catalog-products-list',
    ),
  );
  $form['catalog-products-list']['uc_product_nodes_per_page'] = array(
    '#type' => 'textfield',
    '#title' => t('Product nodes per page'),
    '#default_value' => variable_get('uc_product_nodes_per_page', 12),
    '#description' => t("Determines how many products will be listed on every catalog category. Notice that if you are using grid display it must be multiple of the grid width value, otherwise the last row will not match."),
    '#size' => 2,
  );
  return system_settings_form($form);
}

/**
 * Catalog settings form.
 *
 * Configure the display of the catalog breadcrumb.
 *
 * @ingroup forms
 */
function uc_catalog_grid_admin_form() {
  $form = array();

  // JTR - elements for products grid view administration
  $form['catalog-grid'] = array(
    '#type' => 'fieldset',
    '#title' => t('Products grid display'),
    '#collapsible' => true,
    '#collapsed' => false,
    '#weight' => -3,
    '#attributes' => array(
      'class' => 'catalog-grid',
    ),
  );
  $form['catalog-grid']['uc_catalog_grid_display'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display products in grid'),
    '#default_value' => variable_get('uc_catalog_grid_display', false),
    '#description' => t("Select it if you want the products to be displayed on a grid instead of a list table."),
  );
  $form['catalog-grid']['uc_catalog_grid_display_width'] = array(
    '#type' => 'textfield',
    '#title' => t('Width of grid'),
    '#default_value' => variable_get('uc_catalog_grid_display_width', 3),
    '#size' => 2,
  );
  $form['catalog-grid']['uc_catalog_grid_display_title'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display product title'),
    '#default_value' => variable_get('uc_catalog_grid_display_title', true),
  );
  $form['catalog-grid']['uc_catalog_grid_display_model'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display product model (SKU)'),
    '#default_value' => variable_get('uc_catalog_grid_display_model', true),
  );
  $form['catalog-grid']['uc_catalog_grid_display_sell_price'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display product sell price'),
    '#default_value' => variable_get('uc_catalog_grid_display_sell_price', true),
  );
  $form['catalog-grid']['uc_catalog_grid_display_add_to_cart'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display product add To cart'),
    '#default_value' => variable_get('uc_catalog_grid_display_add_to_cart', true),
  );
  $form['catalog-grid']['uc_catalog_grid_display_attributes'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display attributes'),
    '#default_value' => variable_get('uc_catalog_grid_display_attributes', true),
    '#description' => t('Allows displaying attributes when "Add to cart button" is selected.'),
  );
  return system_settings_form($form);
}

/**
 * Formats the breadcrumb to the current term's ancestry.
 *
 * @param $tid
 *   The currently viewed catalog term's id.
 * @param $is_node
 *   If true, include the current category page in the breadcrumb.
 * @return
 *   An array of breadcrumb links.
 */
function uc_catalog_set_breadcrumb($tid) {
  static $breadcrumbs = array();
  static $terms = array();
  if (variable_get('uc_catalog_breadcrumb', true)) {
    if (empty($breadcrumbs)) {
      if (variable_get('site_frontpage', 'node') != 'catalog') {
        $breadcrumbs[] = l(t('Home'), '');
      }
      if ($tid != 0) {
        $breadcrumbs[] = l(variable_get('uc_catalog_name', t('Catalog')), 'catalog');
      }
    }
    $parents = taxonomy_get_parents_all($tid);

    // Remove current term from the breadcrumb.
    array_shift($parents);
    $types = module_invoke_all('product_types');
    while (count($parents)) {
      $current = array_pop($parents);
      if (!in_array($current->tid, $terms)) {
        $n = 0;
        foreach ($types as $type) {
          $n += taxonomy_term_count_nodes($current->tid, $type);
        }
        $current->nodes = $n;
        $breadcrumbs[] = l($current->name . (variable_get('uc_catalog_breadcrumb_nodecount', false) && $current->nodes ? ' (' . $current->nodes . ')' : ''), uc_catalog_path($current));
        $terms[] = $current->tid;
      }
    }

    //print '<pre>'. print_r($breadcrumbs, true) .'</pre>';
    return $breadcrumbs;
  }
  else {
    return null;
  }
}

/**
 * Display links to all products that have not been categorized.
 */
function uc_catalog_orphaned_products() {
  $output = '<p>' . t('Orphaned products are products that you have created but
     not yet assigned to a category in your product catalog. All such
     products will appear as links below that you can follow to edit
     the product listings to assign them to categories.') . '</p>';
  $query = "SELECT DISTINCT n.nid, n.title FROM {node} AS n LEFT JOIN {term_node} AS tn ON n.nid = tn.nid LEFT JOIN {vocabulary_node_types} AS vnt ON n.type = vnt.type WHERE n.type != 'image' AND tn.tid IS NULL AND vnt.vid = %d";
  $vid = variable_get('uc_catalog_vid', 0);
  $result = db_query($query, $vid);
  $rows = array();
  while ($node = db_fetch_object($result)) {
    $rows[] = l($node->title, 'node/' . $node->nid . '/edit', array(), 'destination=admin/store/products/orphans');
  }
  if (count($rows) > 0) {
    $output .= theme('item_list', $rows);
  }
  else {
    $output .= '<p>' . t('All products are currently listed in the catalog.') . '</p>';
  }
  return $output;
}

/**
 * Emulates Drupal's menu system, but based soley on the structure of "Product Catalog".
 *
 * @param $branch
 *   A treeNode object. Determines if the URL points to itself,
 *   or possibly one of it's children, if present.
 *
 *   If the URL points to itself or one of its products, it displays its name, and
 *   expands to show its children, otherwise displays a link and a count of the products in it.
 *   If the URL points to one of it's children, it still displays a link and product count,
 *   but must still be expanded.
 *   Otherwise, it is collapsed and a link.
 * @return
 *   An array whose first element is true if the treeNode is in hierarchy of the URL path.
 *   The second element is the HTML of the list item of itself and it's children.
 */
function _uc_catalog_navigation($branch) {
  static $terms;
  static $breadcrumb;
  static $types;
  if (empty($types)) {
    $types = module_invoke_all('product_types');
  }
  $branch_path = uc_catalog_path($branch);
  if (!isset($breadcrumb)) {
    $breadcrumb = drupal_get_breadcrumb();
  }
  $vid = variable_get('uc_catalog_vid', 0);
  if ($_GET['q'] == $branch_path) {

    // The URL points to this term.
    $here = true;
  }
  else {
    $here = false;
  }
  if (!isset($terms)) {
    $terms = taxonomy_node_get_terms_by_vocabulary(arg(1), $vid);
  }

  // Determine whether to expand menu item.
  if (arg(0) == 'node' && array_key_exists($branch->tid, $terms)) {
    $inpath = false;
    foreach ($breadcrumb as $link) {
      if (strpos($link, drupal_get_path_alias($branch_path)) !== false) {
        $inpath = true;
      }
    }
  }
  else {
    $inpath = $here;
  }
  $num = 0;
  foreach ($types as $type) {
    $num += taxonomy_term_count_nodes($branch->tid, $type);
  }

  // Checks to see if node counts are desired in navigation
  $num_text = '';
  if (variable_get('uc_catalog_block_nodecount', TRUE)) {
    $num_text = ' (' . $num . ')';
  }
  if (!$num) {

    // No nodes in category or descendants. Not in path and display nothing.
    return array(
      false,
      '',
    );
  }
  $expand = variable_get('uc_catalog_expand_categories', false);
  $link = l($branch->name . $num_text, $branch_path);
  $active_link = l($branch->name . $num_text, $branch_path, array(
    'class' => 'active',
  ));
  if ($expand || count($branch->children)) {
    $lis = array();
    foreach ($branch->children as $twig) {

      // Expand if children are in the menu path. Capture their output.
      list($child_in_path, $lis[]) = _uc_catalog_navigation($twig);
      if ($child_in_path) {
        $inpath = $child_in_path;
      }
    }
  }
  $output = theme("uc_catalog_item", $here, $active_link, $lis, $expand, $inpath, $link, count($branch->children));

  // Tell parent category your status, and pass on output.
  return array(
    $inpath,
    $output,
  );
}

/**
 * Display a formatted link in the catalog block.
 *
 * @ingroup themeable
 * @see theme_uc_catalog_block_start
 * @see theme_uc_catalog_block_end
 */
function theme_uc_catalog_item($here, $active_link = '', $lis, $expand, $inpath, $link, $count_children) {
  $output = '';
  if ($expand || $count_children) {
    if ($here) {
      $output = '<li class="expanded"><strong>' . $active_link . "</strong>\n";
      if (count($lis)) {
        $output .= '<ul class="menu">';
        foreach ($lis as $li) {
          $output .= $li . "\n";
        }
        $output .= "</ul>\n";
      }
      $output .= "</li>";
    }
    elseif ($expand || $inpath) {
      $output = '<li class="expanded">' . ($referer == $branch_path ? '<strong>' : '') . $link . ($referer == $branch_path ? '</strong>' : '') . "\n";
      if (count($lis)) {
        $output .= '<ul class="menu">';
        foreach ($lis as $li) {
          $output .= $li;
        }
        $output .= "</ul>\n";
      }
      $output .= "</li>";
    }
    else {
      $output = '<li class="collapsed">' . $link . "</li>\n";
    }
  }
  else {
    $output = '<li class="leaf">' . ($inpath ? '<strong>' : '') . ($here ? $active_link : $link) . ($inpath ? '</strong>' : '') . '</li>' . "\n";
  }
  return $output;
}

/**
 * Create paths to the catalog from taxonomy term.
 */
function uc_catalog_path($term) {
  return 'catalog/' . $term->tid;
}

/**
 * Load a catalog term's image.
 */
function uc_catalog_image_load($term) {
  if (is_object($term)) {
    $tid = $term->tid;
  }
  else {
    if (is_array($term)) {
      $tid = $term['tid'];
    }
    else {
      $tid = $term;
    }
  }
  $file = db_fetch_object(db_query("SELECT * FROM {uc_catalog_images} WHERE tid = %d", $tid));
  return $file;
}

Functions

Namesort descending Description
theme_uc_catalog_block_end Theme the end of the catalog block.
theme_uc_catalog_block_start Theme the beginning of the catalog block.
theme_uc_catalog_browse Display a formatted catalog page.
theme_uc_catalog_item Display a formatted link in the catalog block.
theme_uc_catalog_products Display a formatted list of products.
theme_uc_catalog_product_grid
uc_catalog_admin_form Catalog settings form.
uc_catalog_admin_settings Determines if the "Product Catalog" vocabulary has been set up.
uc_catalog_block Displays a menu for navigating the "Product Catalog"
uc_catalog_blocks_admin_settings Determines if the "Product Catalog" vocabulary has been set up.
uc_catalog_enable Implementation of hook_enable().
uc_catalog_form_alter Add an image field to the catalog's taxonomy term form.
uc_catalog_get_page Load catalog information for display.
uc_catalog_grid_admin_form Catalog settings form.
uc_catalog_grid_admin_settings Determines if the "Product Catalog" vocabulary has been set up.
uc_catalog_image_load Load a catalog term's image.
uc_catalog_link_alter Implementation of hook_link_alter().
uc_catalog_menu Implementation of hook_menu().
uc_catalog_nodeapi Implementation of hook_nodeapi().
uc_catalog_orphaned_products Display links to all products that have not been categorized.
uc_catalog_path Create paths to the catalog from taxonomy term.
uc_catalog_pathauto
uc_catalog_pathauto_bulkupdate Generate aliases for all categories without aliases
uc_catalog_path_alias_types
uc_catalog_perm Implementation of hook_perm().
uc_catalog_product_class Implementation of Übercart's hook_product_class.
uc_catalog_settings_overview Display an overview of all catalog settings.
uc_catalog_set_breadcrumb Formats the breadcrumb to the current term's ancestry.
uc_catalog_set_category_form Form to change the category of many nodes at once.
uc_catalog_set_category_form_submit Submit function for uc_catalog_set_category_form().
uc_catalog_store_status Implementation of Übercart's hook_store_status().
uc_catalog_taxonomy Implementation of hook_taxonomy().
_uc_catalog_navigation Emulates Drupal's menu system, but based soley on the structure of "Product Catalog".
_uc_catalog_pathauto_alias Function to create aliases for taxonomy objects

Classes

Namesort descending Description
uc_treeNode Data structure to mimic Drupal's menu system.