Only in nat: .DS_Store diff -rup nat-HEAD/nat.module nat/nat.module --- nat-HEAD/nat.module 2007-12-17 00:03:49.000000000 -0800 +++ nat/nat.module 2008-02-05 02:11:53.000000000 -0800 @@ -15,9 +15,11 @@ * o Filter handling for body/description fields. * o Duplicate handling? * - * Features to be added: + * Features recently added: * o Node deletes: Optionally delete child nodes associated via NAT. - * o Maintain hierarchy on unassociated vocabularies (on a best effort basis?) + * o Maintain hierarchy on unassociated vocabularies + * o Sync terms -> nodes, term depth can determine nodetype + * o i18n language for term: Optionally add i18n language data to term */ /** @@ -96,24 +98,19 @@ function nat_nodeapi(&$node, $op, $tease $node->nat = nat_get_terms($node->nid); break; case 'insert': - $body = $node->body; - // Copying over the node body is optional. - $body = isset($nat_config['body'][$node->type]) ? $body : ''; + // if terms already exist then this node was created from a sync + if(isset($node->nat)) { + break; + } // Add node title as terms. - $terms = _nat_add_terms($nat_config['types'][$node->type], $node->title, $body, $node->taxonomy, $node->nat['related'], $node->nat['synonyms']); + $terms = _nat_add_terms($nat_config['types'][$node->type], $node); // Save node-term association in the NAT table. _nat_save_association($node->nid, $terms); break; case 'update': - $body = $node->body; - // Copying over the node body is optional. - $body = isset($nat_config['body'][$node->type]) ? $body : ''; - - // Update node title in term(s). - $terms = nat_get_terms($node->nid); - _nat_update_terms($terms, $node->title, $body, $node->taxonomy, $node->nat['related'], $node->nat['synonyms']); + _nat_update_node_terms($node); break; case 'delete': // Deleting the associated term when a node is deleted is optional. @@ -127,6 +124,26 @@ function nat_nodeapi(&$node, $op, $tease } /** + * Implementation of hook_taxonomy(). + */ +function nat_taxonomy($op, $type, $id) { + + if($op == 'delete' && $type == 'term') { + $config = _nat_variable_get(); + + // term has already been deleted - so nat_get_nids() will not work + $result = db_query("SELECT nat.nid, node.type FROM {nat} nat INNER JOIN {node} node USING (nid) WHERE nat.tid = %d",$id['tid']); + + // delete all nat associated nodetypes set for deletion + while ($node = db_fetch_object($result)) { + if($config['delete_node'][$node->type] == 1) { + node_delete($node->nid); + } + } + } +} + +/** * Implementation of hook_form_alter(). */ function nat_form_alter($form_id, &$form) { @@ -235,6 +252,18 @@ function nat_settings_form() { '#default_value' => isset($nat_config['delete'][$type]) ? $nat_config['delete'][$type] : 0, '#parents' => array('delete', $type) ); + $form['nat_'. $type]['delete_node'. $type] = array( + '#type' => 'checkbox', + '#title' => t('Delete associated node if a term is deleted.'), + '#default_value' => isset($nat_config['delete_node'][$type]) ? $nat_config['delete_node'][$type] : 0, + '#parents' => array('delete_node', $type) + ); + $form['nat_'. $type]['add_language'. $type] = array( + '#type' => 'checkbox', + '#title' => t('Add i18n language information to term.'), + '#default_value' => isset($nat_config['add_language'][$type]) ? $nat_config['add_language'][$type] : 0, + '#parents' => array('add_language', $type) + ); $form['nat_'. $type]['related_'. $type] = array( '#type' => 'checkbox', '#title' => t('Allow users to define synonyms and related terms when they create and edit nodes.'), @@ -261,6 +290,8 @@ function nat_settings_form_submit($form_ $form_values['body'] = array_filter($form_values['body']); $form_values['delete'] = array_filter($form_values['delete']); + $form_values['delete_node'] = array_filter($form_values['delete_node']); + $form_values['add_language'] = array_filter($form_values['add_language']); $form_values['node_links'] = array_filter($form_values['node_links']); $form_values['related'] = array_filter($form_values['related']); @@ -282,30 +313,63 @@ function nat_sync_form() { $nat_config = _nat_variable_get(); $options = array(); + $form['sync'] = array( + '#type' => 'fieldset', + '#title' => t('Sync associations'), + '#description' => t('The Sync operation will create NAT associations (and terms or nodes) for nodes or terms (marked for NAT association) but not present in the NAT table'), + '#collapsible' => TRUE + ); + $form['sync']['terms_to_nodes'] = array( + '#type' => 'fieldset', + '#title' => t('Create nodes from existing terms'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); foreach ($nat_config['types'] as $type => $associations) { if (!empty($associations)) { foreach ($associations as $vid) { - $options[$type .'|'. $vid] = t('@type ‹-› !vocabulary', array('@type' => $type, '!vocabulary' => $vocabularies[$vid])); + $vocab_options[$type .'|'. $vid] = t('Unassociated @type nodes will create --› !vocabulary terms', array('@type' => $type, '!vocabulary' => $vocabularies[$vid])); + + $node_options = array('no'=>'','all' =>'from all vocabulary depths'); + $tree = taxonomy_get_tree($vid); + $maxdepth = _array_depth($tree); + + if($maxdepth) { + while($maxdepth >= 0) { + $node_options[$maxdepth] = t('only from terms at depth') . ' ' . $maxdepth; + --$maxdepth; + } + } + + $form['sync']['terms_to_nodes']["$type"] = array( + '#type' => 'select', + '#title' => t('Unassociated !vocabulary terms will create --› @type nodes', array('@type' => $type, '!vocabulary' => $vocabularies[$vid])), + '#description' => t('Any terms not already NAT associated with the selected node types will be associated.'), + '#required' => FALSE, + '#options' => $node_options + ); + + // $node_options[$type .'|'. $vid] = t('Unassociated !vocabulary terms will create --› @type nodes', array('@type' => $type, '!vocabulary' => $vocabularies[$vid])); } } } - if (empty($options)) { + if (empty($vocab_options)) { drupal_set_message(t('There are no vocabularies available to sync.')); drupal_goto('admin/settings/nat'); } - $form['sync'] = array( + $form['sync']['nodes_to_terms'] = array( '#type' => 'fieldset', - '#title' => t('Sync associations'), - '#description' => t('The Sync operation will create NAT associations (and terms) for nodes (marked for NAT association) not present in the NAT table.'), - '#collapsible' => TRUE + '#title' => t('Create terms from existing nodes'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, ); - $form['sync']['vocabularies'] = array( + $form['sync']['nodes_to_terms']['vocabularies'] = array( '#type' => 'checkboxes', - '#title' => t('Select the vocabularies to sync with associated node tables'), + '#title' => t('Select the node types to sync with associated vocabularies'), '#description' => t('Any nodes not already NAT associated with the selected vocabularies will be associated.'), - '#required' => TRUE, - '#options' => $options + '#required' => FALSE, + '#options' => $vocab_options ); $form['submit'] = array('#type' => 'submit', '#value' => t('Sync tables')); @@ -316,7 +380,8 @@ function nat_sync_form() { * Process NAT sync form submissions. */ function nat_sync_form_submit($form_id, $form_values) { - _nat_sync_associations(array_filter($form_values['vocabularies'])); + _nat_sync_associations_vocab(array_filter($form_values['vocabularies'])); + _nat_sync_associations_nodes($form_values); } /** @@ -550,33 +615,42 @@ function _nat_get_vocabularies() { /** * Add node titles as terms into the taxonomy system. - * @todo Ideas are welcome to allow retaining the hierarchy for vocabularies not - * present in the node form. * * @param $vids * An array of vocabulary IDs denoting the vocabularies into which a NAT term * is to be added. - * @param $title - * Node title. - * @param $body - * Node body. - * @param $hierarchy - * The taxonomy array for the node in question. This is used to, if set, - * insert the new term as a child term of the selected parent. + * @param $node + * Complete node. * @return $tids * An array of term objects. */ -function _nat_add_terms($vids, $title, $body, $hierarchy = array(), $relations = array(), $synonyms = NULL) { +function _nat_add_terms($vids, $node) { + + $nat_config = _nat_variable_get(); + + $title = $node->title; + + // Copying over the node body is optional. + $body = isset($nat_config['body'][$node->type]) ? $node->body : ''; + + $hierarchy = $node->taxonomy; + $relations = isset($node->nat['related']) ? $node->nat['related'] : array(); + $synonyms = $node->nat['synonyms']; + $edit = array( 'name' => $title, 'description' => $body, - 'weight' => 0 + 'weight' => 0, ); if (!empty($synonyms)) { $edit['synonyms'] = $synonyms; } + if (isset($node->language) && $nat_config['add_language'][$node->type] == 1) { + $edit['language'] = $node->language; + } + $tids = array(); foreach ($vids as $vid) { @@ -607,31 +681,52 @@ function _nat_add_terms($vids, $title, $ /** * Update saved node-terms. * - * @param $terms - * An array of existing NAT-term objects. - * @param $title - * Title of the node. - * @param $body - * Body of the node. - * @param $hierarchy - * The taxonomy array for the node in question. This is used to, if set, - * insert the new term as a child term of the selected parent. + * @param $node + * Node for which terms should be updated */ -function _nat_update_terms($terms, $title, $body, $hierarchy, $relations = array(), $synonyms = null) { +function _nat_update_node_terms($node) { + + $nat_config = _nat_variable_get(); + + $terms = nat_get_terms($node->nid); + + // Update node title in term(s). + $title = $node->title; + + // Copying over the node body is optional. + $body = isset($nat_config['body'][$node->type]) ? $node->body : ''; + + $hierarchy = $node->taxonomy; + $relations = isset($node->nat['related']) ? $node->nat['related'] : array(); + $synonyms = $node->nat['synonyms']; + $edit = array( 'name' => $title, 'description' => $body, - 'weight' => 0 ); if (!empty($synonyms)) { $edit['synonyms'] = $synonyms; } + if (isset($node->language) && $nat_config['add_language'][$node->type] == 1) { + $edit['language'] = $node->language; + } + foreach ($terms as $term) { if (isset($hierarchy[$term->vid])) { $edit['parent'] = $hierarchy[$term->vid]; } + elseif(count(taxonomy_get_parents($term->tid))) { + // preserves term's parent if it exists + $parents = taxonomy_get_parents($term->tid); + + // $parents is an object but taxonomy_save_term expects an array + foreach($parents as $parent) { + $parent_array[] = array($parent->tid => $parent->tid); + } + $edit['parent'] = $parent_array; + } else { $edit['parent'] = array(); } @@ -641,6 +736,7 @@ function _nat_update_terms($terms, $titl } $edit['tid'] = $term->tid; + taxonomy_save_term($edit); } } @@ -690,8 +786,8 @@ function _nat_delete_association($nid) { * @param $associations * Associative array denoting the node-vocabulary pair that is to be synced. */ -function _nat_sync_associations($associations) { - $nat_config = _nat_variable_get(); +function _nat_sync_associations_vocab($associations) { + $nat_config = variable_get('nat_config', array()); $counter = 0; foreach ($associations as $association) { @@ -703,7 +799,7 @@ function _nat_sync_associations($associa $body = isset($nat_config['body'][$node['type']]) ? $node['body'] : ''; // Add node title as terms. - $terms = _nat_add_terms(array($association[1]), $node['title'], $body); + $terms = _nat_add_terms(array($association[1]), $node); // Save node-term association in the NAT table. _nat_save_association($node['nid'], $terms); @@ -711,7 +807,117 @@ function _nat_sync_associations($associa $counter++; } } - drupal_set_message(t('NAT sync complete: %count nodes synced.', array('%count' => $counter))); + if($counter > 0) { + drupal_set_message(t('NAT sync complete: %count nodes->terms synced.', array('%count' => $counter))); + } +} + +/** + * Synchronize NAT node-term relationships. Create associated nodes for terms + * where missing. + * + * @param $associations + * Associative array denoting the node-vocabulary pair that is to be synced. + */ +function _nat_sync_associations_nodes($form_values) { + + global $user; + + $vocabularies = _nat_get_vocabularies(); + $nat_config = variable_get('nat_config', array()); + + $counter = array(); + foreach ($nat_config['types'] as $type => $associations) { + if (!empty($associations) && $form_values[$type] != 'no') { + foreach ($associations as $vid) { + + // get terms for vocabulary + $terms = taxonomy_get_tree($vid); + + foreach($terms as $key => $term) { + + if($term->depth == $form_values[$type] || $form_values[$type] == 'all') { + + // find node nid associated with term + $natnid = db_fetch_object(db_query('SELECT n.nid FROM {nat} n WHERE n.tid = %d', $term->tid)); + + // if no associated node - then create and save node + if(!is_numeric($natnid->nid)) { + + // build node + $new_node = (object)$node; + $new_node->type = $type; + node_object_prepare($new_node); + $new_node->title = $term->name; + $new_node->status = 1; + + $parent = taxonomy_get_parents($term->tid); + if($parent) { + $new_node->taxonomy = array('$association[1]' => current($parent)->tid); + } + + $new_node->body = isset($nat_config['body'][$association[0]]) ? $term->description : ''; + $new_node->uid = $user->uid; + $new_node->nat = 1; // set so that new term is not created when node_api hook is called + + // save node + node_save($new_node); + + // save association now that we have a nid + _nat_save_association($new_node->nid, array(array('tid'=>$term->tid,'vid'=>$association[1]))); + + ++$counter[$type]; + } + } + } + } + } + } + + if(count($counter)) { + foreach($counter as $key => $value) { + drupal_set_message(t('NAT sync complete: %value terms --> %key nodes synced.', array('%value' => $value, '%key' => $key))); + } + } +} + +/** + * Find maximum depth of a vocabulary + * + * @param $vid + * vid of the vocabulary + * @return $depth + * An integer with max depth + */ + +function _vocabulary_depth($vid) { + + $tree = taxonomy_get_tree($vid); + $depth = _array_depth($tree); + + return $depth; +} + +/** + * Find maximum depth of an array + * + * @param $array + * Associative array denoting the node-vocabulary pair that is to be synced. + * @param $depthcount + * For internal use + * @return $depthcount + * An integer with max depth + */ + +function _array_depth($array) { + + $depth = 0; + foreach ($array as $value) { + if($value->depth > $depth) { + $depth = $value->depth; + } + } + return $depth; } /** @@ -730,7 +936,9 @@ function _nat_variable_get($name = NULL) 'types' => array(), 'body' => array(), 'delete' => array(), - 'node_links' => array() + 'node_links' => array(), + 'add_language' => array(), + 'delete_node' => array(), ); $variables = variable_get('nat_config', array()); $variables = array_merge($defaults, $variables);