tid = $term->tid; $this->name = $term->name; $this->depth = $term->depth; $this->sequence = $term->sequence; } } /** * Determine 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() { global $user; $items = array(); $items['catalog'] = array( 'title callback' => 'variable_get', 'title arguments' => array('uc_catalog_name', t('Catalog')), 'page callback' => 'theme', 'page arguments' => array('uc_catalog_browse'), 'access arguments' => array('view catalog'), 'type' => MENU_SUGGESTED_ITEM, 'file' => 'uc_catalog.pages.inc', ); $items['admin/store/settings/catalog'] = array( 'title' => 'Catalog settings', 'description' => 'Configure the catalog settings.', 'page callback' => 'uc_catalog_settings_overview', 'access arguments' => array('administer catalog'), 'type' => MENU_NORMAL_ITEM, 'file' => 'uc_catalog.admin.inc', ); $items['admin/store/settings/catalog/overview'] = array( 'title' => 'Overview', 'access arguments' => array('administer catalog'), 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'uc_catalog.admin.inc', ); $items['admin/store/settings/catalog/edit'] = array( 'title' => 'Edit', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_catalog_settings_form'), 'access arguments' => array('administer catalog'), 'weight' => -5, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_catalog.admin.inc', ); $items['admin/store/settings/catalog/edit/catalog'] = array( 'title' => 'Catalog settings', 'weight' => -10, 'access arguments' => array('administer catalog'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'uc_catalog.admin.inc', ); $items['admin/store/settings/catalog/edit/grid'] = array( 'title' => 'Product grid settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_catalog_grid_settings_form'), 'access arguments' => array('administer catalog'), 'weight' => -5, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_catalog.admin.inc', ); $items['admin/store/products/orphans'] = array( 'title' => 'Find orphaned products', 'description' => 'Find products that have not been categorized.', 'page callback' => 'uc_catalog_orphaned_products', 'access arguments' => array('administer catalog'), 'weight' => -4, 'type' => MENU_NORMAL_ITEM, 'file' => 'uc_catalog.admin.inc', ); return $items; } /** * Implementation of hook_perm(). */ function uc_catalog_perm() { return array('view catalog', 'administer catalog'); } /** * Implementation of hook_init(). */ function uc_catalog_init() { global $conf; $conf['i18n_variables'][] = 'uc_catalog_description'; $conf['i18n_variables'][] = 'uc_catalog_name'; } /** * Implementation of hook_enable(). * * Add imagecache preset "uc_category". */ function uc_catalog_enable() { foreach (uc_product_types() as $type) { uc_catalog_add_node_type($type); } } /** * Implementation of hook_imagecache_default_presets(). */ function uc_catalog_imagecache_default_presets() { $presets = array(); $presets['uc_category'] = array( 'presetname' => 'uc_category', 'actions' => array( array( 'weight' => '0', 'module' => 'uc_category', 'action' => 'imagecache_scale', 'data' => array( 'width' => '100', 'height' => '100', 'upscale' => 0, ), ), ), ); return $presets; } /** * Implementation of hook_theme(). */ function uc_catalog_theme() { return array( 'uc_catalog_block' => array( 'arguments' => array('menu_tree' => NULL), ), 'uc_catalog_browse' => array( 'arguments' => array('tid' => 0), 'file' => 'uc_catalog.pages.inc', ), 'uc_catalog_products' => array( 'arguments' => array('products' => NULL), ), 'uc_catalog_product_grid' => array( 'arguments' => array('products' => NULL), ), 'uc_catalog_item' => array( 'arguments' => array( 'here' => NULL, 'link' => NULL, 'lis' => NULL, 'expand' => NULL, 'inpath' => NULL, 'count_children' => NULL, ), ), ); } /** * Implementation of hook_nodeapi(). */ function uc_catalog_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { static $parents = array(); if (uc_product_is_product($node->type)) { 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, 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('
'. print_r($parents[$term->tid], TRUE) .''); 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_save_upload($field_name)) { $file->filepath = str_replace('\\', '/', $file->filepath); $image_path = file_create_path(); if (file_copy($file, $image_path .'/'. $file->filename)) { file_set_status($file, 1); 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)", $file->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']); } elseif ($file = file_save_upload($field_name)) { $file->filepath = str_replace('\\', '/', $file->filepath); //dpm($file); if (file_copy($file, file_create_path() .'/'. $file->filename)) { file_set_status($file, 1); //dpm($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)", $file->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; } } /** * Implementation of hook_form_alter(). * * Add an image field to the catalog's taxonomy term form. */ function uc_catalog_form_alter(&$form, $form_state, $form_id) { if ($form_id == 'taxonomy_form_term' && $form['vid']['#value'] == variable_get('uc_catalog_vid', 0)) { $form['#attributes'] = array("enctype" => "multipart/form-data"); $form['identification']['name']['#weight'] = -1; $form['identification']['image']['#weight'] = 0; $form['identification']['image']['image'] = array('#type' => 'file', '#title' => t('Image'), '#weight' => 0, ); $image = uc_catalog_image_load($form['tid']['#value']); $imagecache = module_exists('imagecache'); if ($image) { if ($imagecache) { $image_display = theme('imagecache', 'uc_category', $image->filepath); } else { $image_display = theme('image', $image->filepath, t('Term image')); } $form['identification']['image']['remove'] = array('#type' => 'checkbox', '#title' => t('Remove category image: !image', array('!image' => $image_display)), '#weight' => 1, ); } if (!$imagecache) { $form['identification']['image']['image']['#description'] = t('The image will not be resized. Consider installing Image cache.', array('@url' => url('http://drupal.org/project/imagecache'))); } $form['identification']['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(&$links, $node) { // 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); } } } } /** * Implementation of hook_block(). * * Displays a menu for navigating the "Product Catalog" */ function uc_catalog_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': $blocks[0]['info'] = variable_get('uc_catalog_name', t('Catalog')); $blocks[0]['cache'] = BLOCK_CACHE_PER_PAGE; return $blocks; case 'view': $block = array(); if (user_access('view catalog')) { switch ($delta) { case 0: drupal_add_css(drupal_get_path('module', 'uc_catalog') .'/uc_catalog.css'); // 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 UcTreeNode(); $level = array(); $curr_depth = -1; foreach ($tree as $knot) { $seq++; $knot->sequence = $seq; $knothole = new UcTreeNode($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', $menu_tree); $subject = variable_get('uc_catalog_name', t('Catalog')); if (variable_get('uc_catalog_block_title', FALSE)) { $subject = l($subject, 'catalog'); } $block = array('subject' => $subject, 'content' => $content); break; } } return $block; } } /** * Theme the catalog block. * * @ingroup themeable * @see theme_uc_catalog_item() */ function theme_uc_catalog_block($menu_tree) { $output = ' '; return $output; } /****************************************************************************** * Module Hooks * ******************************************************************************/ /** * Implementation of hook_pathauto(). */ 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; } } /** * Implementation of hook_pathauto_bulkupdate(). * * 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.")); } /** * Create aliases for taxonomy objects * * @param $category * A taxonomy object */ function _uc_catalog_pathauto_alias($category, $op) { module_load_include('inc', 'pathauto'); $count = 0; if ($category->vid == variable_get('uc_catalog_vid', 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; } /** * Implementation of hook_path_alias_types(). */ function uc_catalog_path_alias_types() { return array('catalog/' => t('catalog pages')); } /****************************************************************************** * Ubercart Hooks * ******************************************************************************/ /** * Implementation of 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_vocabulary_load($cat_id); if ($catalog) { $statuses[] = array('status' => 'ok', 'title' => t('Catalog vocabulary'), 'desc' => t('Vocabulary !name has been identified as the Ubercart catalog.', array('!name' => l($catalog->name, 'admin/content/taxonomy/'. $catalog->vid))) ); $excluded = 0; $result = db_query("SELECT COUNT(DISTINCT n.nid) 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); if ($excluded = db_result($result)) { $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 Product Catalog 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 this page or add one on the taxonomy admin page first, if needed.', array('!admin_catalog' => url('admin/store/settings/catalog/edit'), '!admin_vocab' => url('admin/content/taxonomy'))), ); } return $statuses; } /** * Implementation of 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 * ******************************************************************************/ /** * 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)) { $info = image_get_info($file->filepath); $catalog->image = $info; $catalog->image['filepath'] = $file->filepath; } $types = uc_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 ($file = uc_catalog_image_load($child->tid)) { $info = image_get_info($file->filepath); $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 list of products. * * @param $products * An array of product nids. * @ingroup themeable */ function theme_uc_catalog_products($products) { if (!$products) { $output .= '
'; if (variable_get('uc_catalog_grid_display_title', TRUE)) { $product_table .= ''. $titlelink .''; } if (variable_get('uc_catalog_grid_display_model', TRUE)) { $product_table .= ''. $product->model .''; } $product_table .= ''. $imagelink .''; if (variable_get('uc_catalog_grid_display_sell_price', TRUE)) { $product_table .= ''. uc_price($product->sell_price, $context) .''; } if (module_exists('uc_cart') && 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 .= ' | '; $count++; } $product_table .= "
'. print_r($breadcrumbs, TRUE) .''; return $breadcrumbs; } else { return NULL; } } /** * Emulate 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 = uc_product_types(); } $num = 0; foreach ($types as $type) { $num += taxonomy_term_count_nodes($branch->tid, $type); } // No nodes in category or descendants. Not in path and display nothing. if (!$num) { return array(FALSE, ''); } $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(menu_get_object('node', 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; } // Checks to see if node counts are desired in navigation $num_text = ''; if (variable_get('uc_catalog_block_nodecount', TRUE)) { $num_text = ' ('. $num .')'; } $lis = array(); $expand = variable_get('uc_catalog_expand_categories', FALSE); $link = l($branch->name . $num_text, $branch_path); if ($expand || count($branch->children)) { 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, $link, $lis, $expand, $inpath, 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 */ function theme_uc_catalog_item($here, $link, $lis, $expand, $inpath, $count_children ) { $output = ''; if ($expand || $count_children) { if ($here) { $output = '