? .hg ? .hgignore ? navigation_fields.patch ? views/handlers/views_handler_field_draggableviews_navigation.inc Index: draggableviews-view-draggabletable-form.tpl.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews-view-draggabletable-form.tpl.php,v retrieving revision 1.6.2.1 diff -u -p -r1.6.2.1 draggableviews-view-draggabletable-form.tpl.php --- draggableviews-view-draggabletable-form.tpl.php 9 Jan 2009 15:02:30 -0000 1.6.2.1 +++ draggableviews-view-draggabletable-form.tpl.php 3 Jun 2009 17:20:49 -0000 @@ -9,5 +9,5 @@ // the view returned by views module print $view; -// submit form +// submit form print $submit_form; Index: draggableviews-view-draggabletable.tpl.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews-view-draggabletable.tpl.php,v retrieving revision 1.6.2.5 diff -u -p -r1.6.2.5 draggableviews-view-draggabletable.tpl.php --- draggableviews-view-draggabletable.tpl.php 25 Apr 2009 12:47:02 -0000 1.6.2.5 +++ draggableviews-view-draggabletable.tpl.php 3 Jun 2009 17:20:49 -0000 @@ -8,18 +8,18 @@ * - $fields: An array of CSS IDs to use for each field id. * - $class: A class or classes to apply to the table, based on settings. * - $rows: An array of row items. Each row is an array of content - * keyed by field ID. - * - * - $tabledrag: An array of tabledrag settings + * keyed by field ID. + * + * - $tabledrag: An array of tabledrag settings * - $tabledrag_tableId: The table id that drupal_add_tabledrag needs * @ingroup views_templates - */ - - //add tabledrag - if (count($tabledrag) > 0) { - foreach ($tabledrag as $drag) { - drupal_add_tabledrag($tabledrag_table_id, $drag['action'], $drag['relationship'], $drag['group'], $drag['subgroup'], $drag['source'], $drag['hidden'], $drag['limit']); - } + */ + + //add tabledrag + if (count($tabledrag) > 0) { + foreach ($tabledrag as $drag) { + drupal_add_tabledrag($tabledrag_table_id, $drag['action'], $drag['relationship'], $drag['group'], $drag['subgroup'], $drag['source'], $drag['hidden'], $drag['limit']); + } } ?> @@ -37,8 +37,8 @@ $content): ?> - Index: draggableviews.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews.inc,v retrieving revision 1.7.2.27 diff -u -p -r1.7.2.27 draggableviews.inc --- draggableviews.inc 22 May 2009 19:38:53 -0000 1.7.2.27 +++ draggableviews.inc 3 Jun 2009 17:20:49 -0000 @@ -1,899 +1,899 @@ - view, - * - * 'order' => array( - * 'field' => array( - * 'field_name' => field_name, - * 'field_alias' => field_alias, - * 'handler' => handler, - * ), - * 'visible' => TRUE/FALSE, - * ), - * - * 'hierarchy' => array( - * 'field' => array( - * 'field_name' => field_name, - * 'field_alias' => field_alias, - * 'handler' => handler, - * ), - * 'visible' => TRUE/FALSE, - * ), - * - * 'depth_limit' => depth_limit, - * - * 'types' = array( - * node_type1 => "root"/"leaf", - * node_type2 => "root"/"leaf", - * .. - * ), - * - * 'expand_links' = array( - * 'show' => TRUE/FALSE, - * 'default_collapsed' => TRUE/FALSE, - * 'by_uid' => TRUE/FALSE, - * ), - * - * 'view_window_extensions' = array( - * 'extension_top' => 3, - * 'extension_bottom' => 3, - * ), - * - * 'locked' => TRUE/FALSE, - * - * 'depth' => 0, - * - * 'default_on_top' => TRUE/FALSE, - * - * 'nodes' => array( - * nid1 => array( - * 'order' => array( - * 0 => value, - * 1 => value, - * .. - * ), - * 'parent' => value, - * ), - * .. - * ), - * ); - */ -function _draggableviews_info($view, $info = NULL) { - $options = $view->style_plugin->options; - $fields = $view->field; - $results = $view->result; - - // if there is already an info array just rebuild the nodes array and skip this section - if (!isset($info)) { - $info = array(); - - // extract draggableviews settings. - if ($options['tabledrag_order']['field'] != 'none') { - if ($handler = _draggableviews_init_handler($options['tabledrag_order'], $view)) { - $info['order'] = array( - 'field' => array( - 'handler' => $handler, - 'field_name' => $options['tabledrag_order']['field'], - 'field_alias' => $fields[$options['tabledrag_order']['field']]->field_alias, - ), - 'visible' => strcmp($options['tabledrag_order_visible']['visible'], 'visible') == 0 ? TRUE : FALSE, - ); - } - else { - drupal_set_message(t('Draggableviews: Handler ') . $options['tabledrag_order']['handler'] . t(' could not be found.'), 'error'); - unset($info['order']); - return $info; - } - $info['order']['visible'] = strcmp($options['tabledrag_order_visible']['visible'], 'visible') == 0 ? TRUE : FALSE; - - if ($options['tabledrag_hierarchy']['field'] != 'none') { - if ($handler = _draggableviews_init_handler($options['tabledrag_hierarchy'], $view)) { - $info['hierarchy'] = array( - 'field' => array( - 'handler' => $handler, - 'field_name' => $options['tabledrag_hierarchy']['field'], - 'field_alias' => $fields[$options['tabledrag_hierarchy']['field']]->field_alias, - ), - 'visible' => strcmp($options['tabledrag_hierarchy_visible']['visible'], 'visible') == 0 ? TRUE : FALSE, - ); - - $info['depth_limit'] = $options['draggableviews_depth_limit']; - if (!isset($info['depth_limit'])) $info['depth_limit'] = 10; - - if (isset($options['tabledrag_types'])) { - foreach ($options['tabledrag_types'] as $type) { - $info['types'][$type['node_type']] = $type['type']; - } - } - - $info['expand_links'] = array( - 'show' => strcmp($options['tabledrag_expand']['expand_links'], 'expand_links') == 0 ? TRUE : FALSE, - 'default_collapsed' => strcmp($options['tabledrag_expand']['collapsed'], 'collapsed') == 0 ? TRUE : FALSE, - 'by_uid' => strcmp($options['tabledrag_expand']['by_uid'], 'by_uid') == 0 ? TRUE : FALSE, - ); - } - else { - drupal_set_message(t('Draggableviews: Handler ') . $options['tabledrag_hierarchy']['handler'] . t(' could not be found.'), 'error'); - } - } - - $info['view_window_extensions'] = $options['draggableviews_extensions']; - if (!isset($info['view_window_extensions']['extension_top'])) $info['view_window_extensions']['extension_top'] = 3; - if (!isset($info['view_window_extensions']['extension_bottom'])) $info['view_window_extensions']['extension_bottom'] = 3; - - $info['locked'] = strcmp($options['tabledrag_lock']['lock'], 'lock') == 0 ? TRUE : FALSE; - } - } - - $info['depth'] = 0; - - $info['default_on_top'] = $options['draggableviews_default_on_top'] == 1 ? TRUE : FALSE; - - // Hold a reference of the view object itself. - $info['view'] = &$view; - - // Get all nodes and their properties. - $info['nodes'] = array(); - if (isset($info['order']) && isset($results) && count($results) > 0) { - // loop through all resulting nodes - foreach ($results as $row) { - if (is_numeric($row->{$info['order']['field']['field_alias']})) { - $info['nodes'][$row->nid]['order'][0] = (int)($row->{$info['order']['field']['field_alias']}); - } - else { - // Default position of new nodes. - $info['nodes'][$row->nid]['order'][0] = $info['default_on_top'] == 1 ? -1 : $view->total_rows; - } - if (isset($info['hierarchy'])) { - $info['nodes'][$row->nid]['parent'] = (int)($row->{$info['hierarchy']['field']['field_alias']}); - } - } - } - - return $info; -} - -/* - * Quick Check Structure - * - * I used the word "Quick" because only visible nodes and only the very top hierarchy level will be checked. If the - * return is TRUE we can be sure that the structure will be displayed correctly. But errors that don't affect the - * current output can not be detected. Enhanced checks will be done when we have to rebuild a broken structure. - * - * We check for the following: - * - Wrong order values: The order values must constantly increase by 1, independent of the hierarchy level. - * - Parent mismatch: The parent_nid must equal with the nid we memorized before we entered the current hierarchy level. - * - * @param $inputs - * The structured info array. Look at _draggableviews_info(..) to learn more. - * - * return - * TRUE if structure is valid - */ -function _draggableviews_quick_check_structure($info) { - // Calculate views page offset. - $pager = $info['view']->pager; - $offset = $pager['items_per_page'] * $pager['current_page'] + $pager['offset']; - - // Call function in checking mode (renumber = FALSE). The order value must begin with $offset. - return _draggableviews_ascending_numbers($info, $offset, FALSE); -} - -/** - * Build hierarchy - * - * Although there shouldn't be any structure based errors after submit - * broken parents can be detected and repaired. All other structure based errors - * can not be detected. If there are any they will be detected by _draggableviews_quick_check_structure(..) - * and finally repaired by _draggableviews_analyze_structure(..). - * - * @param $info - * The structured information array - */ -function _draggableviews_build_hierarchy(&$info) { - $nodes = &$info['nodes']; - $input = &$info['input']; - - foreach ($nodes as $nid => $prop) { - // get depth - if (($depth = _draggableviews_get_hierarchy_depth($nid, $input, $info)) === FALSE) { - // Error! The hierarchy structure is broken and could - // look like the following: (we're currently processing X) - // A - // --X - // --D - // - // The next steps: - // 1) bring it down to the root level - // 2) Set order fields to the minimum - - $input[$nid]['parent'] = 0; - - // We gracefully sidestep the order-loop - $depth = -1; - } - - // Let's take a look at the following expample, to understand - // what is beeing done. - // - // A - // --B - // --C - // --X - // --D - // E - // Imagine we're currently processing X: - // - // We know that X is in depth=3, so we save the received - // weight value in the 3rd order field of node X. - // - // The 2nd order field must inherit the received weight of - // node C (the next parent). And the 1st order field must - // inherit the received weight of node A (the parent of C). - // - // When we finally order the view by weight1, weight2, weight3 then - // weight1 and weight2 from node X will always equal with - // those from node A and B, and weight3 defines the order of the 3rd level. - - $temp_nid = $nid; - - for ($i = $depth; $i >= 0; $i--) { - // we're operating top-down, so we determine the parents nid by the way - - $nodes[$nid]['order'][$i] = $input[$temp_nid]['order'][0]; - - if (isset($info['hierarchy']) && $i > 0) { - if (!($temp_nid = $input[$temp_nid]['parent'])) { - // this line should never be reached assumed the depth - // was calculated correctly. - drupal_set_message(t('Undefined State called in draggableviews_build_hierarchy(..)'), 'error'); - break; - } - } - } - - if (isset($info['hierarchy'])) { - // Simply set the parent value - $nodes[$nid]['parent'] = $input[$nid]['parent']; - } - - // Now set the next level to the minimum value. Otherwise - // it could happen that a child appears above its parent. - // The ? can be anything, unfortunately also > 5 - // - // --A (3,5) - // B (3,?) - // - // To guaranteer that the ? is always the lowest, we choose - // the minimum. - - $depth = ($depth == -1) ? 0 : $depth; - - $nodes[$nid]['order'][$depth + 1] = $info['order']['field']['handler']->get_minimum_value(); - $nodes[$nid]['depth'] = $depth; - } - - // Last but not least sort nodes and finally assign ascending numbers. This is necessary since this module supports paging. - _draggableviews_sort_nodes($nodes); - // calculate views page offset - $pager = $info['view']->pager; - $offset = $pager['items_per_page'] * $pager['current_page'] + $pager['offset']; - _draggableviews_ascending_numbers($info, $offset, TRUE); -} - -/** - * Rebuild hierarchy - * - * This function is called when the structure is broken. - * - * @param $info - * The structured information array. Look at _draggableviews_info(..) to learn more. - */ -function _draggableviews_rebuild_hierarchy(&$info) { - // We backup the page settings and restore them after completing all operations. - $backup_pager = $info['view']->pager; - - if ($backup_pager['items_per_page'] > 0) { - // We have to make sure that there's no hidden node with an order value that refers to the current page. - // If the items to display per page are limitated we load the entire view. - - //@todo: We reduce the fields to a minimum because of performance issues. - - _draggableviews_reload_info($info, 0, 0, 0); - } - - // Calculate depth values. - // Nodes with broken parents will be brought down to the root level. - // These depth values will be used for both theming and repairing broken structures. - _draggableviews_calculate_depths($info); - - // Detect and repair ordering errors. - // The nodes order values have to equal with the parents order values. - _draggableviews_check_order($info); - - // The last issue we have to deal with is the order itself. Probably there - // are more many with the same order value what could lead to display errors. - // We just sort the nodes by their current order values and subsequent we assign ascending numbers. - _draggableviews_sort_nodes($info['nodes']); - _draggableviews_ascending_numbers($info, 0, TRUE); - - // Save hierarchy. - _draggableviews_save_hierarchy($info); - - // The structure should be valid now. - // Nonetheless let's make a final check for debugging reasons. - if (!_draggableviews_quick_check_structure($info)) { - drupal_set_message("Draggableviews: Rebuilding structure didn't work. The structure is broken.", "error"); - } -} - -/** - * (CHECK/WRITE) Ascending Numbers. - * - * This function is used for both renumbering and checking. - * Order values have to start with $offset. - * - * @param $info - * The structured info array. Look at _draggableviews_info(..) to learn more. - * @param $offset - * Where we start to count. - * @param $renumber - * Renumber or check. - * - * @return - * TRUE if no errors found. - */ -function _draggableviews_ascending_numbers(&$info, $offset = 0, $renumber = FALSE) { - // We need to hold - // 1) the last nid, - // 2) the last order value, - // 3) the parent's nid of each hierarchy level, - // 3a) the current depth (hierarchy level), - $last_nid = -1; - $last_order = $offset; - $last_parent_nid = array(0); - $depth = 0; - - foreach ($info['nodes'] as $nid => $values) { - $parent_nid = isset($info['hierarchy']) ? $values['parent'] : 0; - - // Let's take a look at the following expample, to understand what is beeing done. - // The order value of the parent's level always equals with the parent's value. - // - // A (0 - -) First node of level0. - // --B (0 1 -) First node of level1. But we continue counting. - // --C (0 1 2) First node of level2. But we still continue counting. - // X (3 - -) Now we leave 2 levels (level2->level0). We still have to continue counting. - // - // Imagine we are currently processing X. We are leaving 2 levels and we need to determine the - // last order value that was used. First we check how many levels are beeing left: - $leave_hierarchy_levels = 0; - for ($i = 0; $i < $depth; $i++) { - if ($parent_nid == $last_parent_nid[$i]) { - // Found the level we go to. Calculate the number of levels that are beeing left. - $leave_hierarchy_levels = $depth - $i; - break; - } - } - if ($leave_hierarchy_levels > 0) { - // We leave some hierarchy levels. We simply inherit the order value of the level we come from. - $depth -= $leave_hierarchy_levels; - // Remove obsoleted values from stack. - for ($i = 0; $i < $leave_hierarchy_levels; $i++) { - array_pop($last_parent_nid); - } - } - else if ($parent_nid == $last_nid) { - // We are entering a new level. This is the first child of the last node. - array_push($last_parent_nid, $last_nid); - $depth++; - } - else if ($parent_nid != $last_parent_nid[$depth]) { - // This node is neither a member of any previous hierarchy level - // nor a child of the last node (opening a new level) - // nor a member of the current hierarchy level - // There's something wrong! - if ($renumber) { - drupal_set_message("Something wrong in _draggableviews_ascending_numbers (". ($renumber ? 'WRITE' : 'CHECK') .").", "error"); - } - return FALSE; - } - - // This function is used for both renumbering and checking the hierarchy. Decide what we have to do: - if ($renumber) { - // Assign order value. - $info['nodes'][$nid]['order'][0] = $last_order; - } - else { - // Check structure. - $order = $values['order'][0]; - if ($order != $last_order) { - // This would cause troubles with paging. - // We better initiate a rebuild! - return FALSE; - } - } - - $last_nid = $nid; - $last_order++; - } - - return TRUE; -} - -/** - * Extend View Window - * - * In order to make it possible to drag from one page to another we have to extend the visible window of each page. - * We also have to make sure that parent nodes appear with all their children (..and child nodes with their parent). - * The view will be re-executed with the calculated range, based on the suggested range. - * - * @param $info - * The structured info array. Look at _draggableviews_info(..) to learn more. - * @param $extension_top - * @param $extension_bottom - * - * @return - * An array containing the new, calculated range. - */ -function _draggableviews_extend_view_window(&$info, $reload = FALSE) { - // Check page settings. - if ($info['pager']['items_per_page'] <= 0) { - // Nothing to do. Just return the actual range. - return array( - 'first_index' => 0, - 'last_index' => count($info['view']->result) - 1, - ); - } - - // Notice that we have two sets of pager information: - // - The "should-be" values at $info['pager'] - // - The real, current values at $view->pager. - // For building the new view we always use the "should-be" pager information. - - $items_per_page = $info['pager']['items_per_page']; - $current_page = $info['pager']['current_page']; - $offset = $info['pager']['offset']; - - $first_index = $items_per_page * $current_page; - $last_index = $items_per_page * ($current_page + 1) - 1; - - $nodes = $info['nodes']; - - // If the new index is out of range we'll use the last existing index. Calculate this index: - $current_first_index = $info['view']->pager['items_per_page'] * $info['view']->pager['current_page'] + $info['view']->pager['offset']; - $total_index = $current_first_index + count($nodes) - 1; - - if ($first_index > $total_index) { - // $first_index is out of range. - $first_index = 0; - $last_index = $items_per_page -1; - } - - // Check permissions and lock. - if (!user_access('Allow Reordering') || $info['locked']) { - // Don't extend view window (= suggestion). - $extension_top = 0; - $extension_bottom = 0; - } - else { - $extension_top = $info['view_window_extensions']['extension_top']; - $extension_bottom = $info['view_window_extensions']['extension_bottom']; - } - - // Extend on top: - $first_index -= $extension_top; - if ($first_index < 0) { - // Be careful: $first_index < 0. So this is a subtraction! - $extension_top += $first_index; - if ($extension_top < 0) $extension_top = 0; - $first_index = 0; - } - // Extend on bottom: - $last_index += $extension_bottom; - if ($last_index > $total_index) { - $extension_bottom = $extension_bottom - ($last_index - $total_index); - if ($extension_bottom < 0) $extension_bottom = 0; - $last_index = $total_index; - } - - $add_to_extension_top = 0; - $add_to_extension_bottom = 0; - - if (isset($info['hierarchy'])) { - // There are possibly some child nodes that would be cut of their parents. To avoid this we localize - // the next parents (if needed) and use their index for the range. To analyze the nodes array we slice out - // all nodes before (the B's) and all nodes after (the a's) the current page (the -'s): - // - // B B B B B B B B B - - - - - - - - A A A A A A A A A A. - // - // Slice out all nodes before (currently not shown): - $nodes_before = array_slice($nodes, 0, $first_index, TRUE); - // Slice out all nodes after (currently not shown): - // array_slice: In PHP version < 5.0.2 the 3rd argument (length) must not be ommited (set to NULL). The value must be the exact amount of elements. - $nodes_after = array_slice($nodes, $last_index + 1, strlen($nodes) + $last_index, TRUE); - // Slice out all nodes that are currently shown: - $nodes_current = array_slice($nodes, $first_index, $last_index - $first_index + 1, TRUE); - - $first_node = current($nodes_current); - if ($first_node['parent'] > 0) { - // The first node of the current page is a child node. Look for the next parent - // that is on the root level. We can't use foreach() because we iterate the wrong way. - $temp_node = end($nodes_before); - while ($temp_node !== FALSE) { - $add_to_extension_top++; - if ($temp_node['parent'] == 0) break; - $temp_node = prev($nodes_before); - } - } - // Check for possible children of the last node. - foreach ($nodes_after AS $temp_node) { - if ($temp_node['parent'] == 0) break; - $add_to_extension_bottom++; - } - - // Update range. - $first_index -= $add_to_extension_top; - $last_index += $add_to_extension_bottom; - } - - if ($reload) { - // Re-execute the view and reload the info array. We need to add 1 to the number of items - // to show (e.g. show from index 5 to index 5: show 5-5+1=1 item). - _draggableviews_reload_info($info, $last_index - $first_index + 1, 0, $offset + $first_index); - } - - // Mark extended nodes as "extension". - $nodes = &$info['nodes']; - for ($i = 0; $i < $extension_top + $add_to_extension_top; $i++) { - $nid = key($nodes); - $nodes[$nid]['extension'] = TRUE; - next($nodes); - } - end($nodes); - for ($i = 0; $i < $extension_bottom + $add_to_extension_bottom; $i++) { - $nid = key($nodes); - $nodes[$nid]['extension'] = TRUE; - prev($nodes); - } - - return array( - 'first_index' => $first_index, - 'last_index' => $last_index, - ); -} - -/** - * Click Sort - * - * The nodes are already in the right order. - * Simply assign ascending values. - * Re-execute view and refresh info array. - * - * @param $info - * The structured info array. Look at _draggableviews_info(..) to learn more. - */ -function _draggableviews_click_sort(&$info) { - // Check permissions. - if (!user_access('Allow Reordering')) { - drupal_set_message(t('You are not allowed to reorder nodes.'), 'error'); - return; - } - - // Check if structure is locked. - if ($info['locked']) { - drupal_set_message(t('The structure is locked.'), 'error'); - return; - } - - // If the items to display per page are limited we load the entire view. - $pager = $info['view']->pager; - if ($pager['items_per_page'] > 0) { - _draggableviews_reload_info($info, 0, 0, 0); - } - - // First bring all child nodes down to the root level. We can't keep the hierarchy information - // because the nodes have been mixed up thoroughly. - if (isset($info['hierarchy'])) { - foreach ($info['nodes'] AS $nid => $values) { - $info['nodes'][$nid]['parent'] = 0; - $info['nodes'][$nid]['depth'] = 0; - } - } - // Assign ascending numbers - _draggableviews_ascending_numbers($info, 0, TRUE); - _draggableviews_save_hierarchy($info); - - // Re-execute view with original page settings. - _draggableviews_reload_info($info, $pager['items_per_page'], $pager['current_page'], $pager['offset']); -} - -/** - * Save Hierarchy - * - * @param $info - * The structured information array. Look at _draggableviews_info(..) to learn more. - */ -function _draggableviews_save_hierarchy($info) { - // Loop through all nodes. - foreach ($info['nodes'] as $nid => $prop) { - if (isset($info['hierarchy'])) { - $value = $prop['parent']; - $handler = $info['hierarchy']['field']['handler']; - $handler->save($nid, $value); - } - - $value = $prop['order'][0]; - $handler = $info['order']['field']['handler']; - $handler->save($nid, $value); - } -} - -/** - * Calculate Depths - * - * Nodes with broken parents will be brought down to the root level. - * - * @param $info - * The structured information array. Look at _draggableviews_info(..) to learn more. - * - * @return - * TRUE if no errors occured. - */ -function _draggableviews_calculate_depths(&$info) { - $error = FALSE; - - // Loop through all rows the view returned. - foreach ($info['nodes'] as $nid => $prop) { - if ($info['nodes'][$nid]['parent'] < 0) $info['nodes'][$nid]['parent'] = 0; - - // Determine hierarchy depth of current row. - $depth = _draggableviews_get_hierarchy_depth($nid, $info['nodes'], $info); - - if ($depth === FALSE) { - $error = TRUE; - $depth = 0; - $info['nodes'][$nid]['parent'] = 0; - } - - $info['nodes'][$nid]['depth'] = $depth; - - // Move the order value to the calculated level. - $info['nodes'][$nid]['order'][$depth] = $info['nodes'][$nid]['order'][0]; - - // Set the next level to the minimum value. Otherwise - // it could happen that a child appears above its parent. - $info['nodes'][$nid]['order'][$depth + 1] = $info['order']['field']['handler']->get_minimum_value(); - - if ($depth > $info['depth']) $info['depth'] = $depth; - } - - return !$error; -} - -/** - * Get Hierarchy Depth - * - * This function detects broken parents. - * Cycles are detected (if a child is the parent of its parent). - * - * @param $nid - * The nid of the node which should be processed. - * @param $nodes - * The nodes array. This can be both a nodes array or an input array. - * They share the same hierarchy properties. - * @param $info - * The structured info array. Look at _draggableviews_info(..) to learn more. - * - * @return - * The hierarchy depth if no occured, else FALSE. - */ -function _draggableviews_get_hierarchy_depth($nid, $nodes, $info) { - if (!isset($info['hierarchy'])) return 0; - - $depth = 0; - $error = FALSE; - $temp_nid = $nid; - $used_nids = array($temp_nid); - - while (!($error = !isset($nodes[$temp_nid])) && ($temp_nid = $nodes[$temp_nid]['parent']) > 0) { - // In order to detect cycles we use an array, - // where all used nids are saved in. If a node id - // occurs twice -> return FALSE. - $cycle_found = array_search($temp_nid, $used_nids); - // The isset(..) is necessary because in PHP-Versions <= 4.2.0 array_search() returns NULL - // instead of FALSE if $temp_nid was not found. - if (isset($cycle_found) && $cycle_found !== FALSE) { - drupal_set_message(t("Draggableviews: A cycle was found.")); - return FALSE; - } - - $used_nids[] = $temp_nid; - $depth++; - } - - if ($error) { - // If loop breaked caused by an error. - return FALSE; - } - - return $depth; -} - -/** - * Detect and Repair Order Values - * - * The nodes order value has to equal with the parents order value. - * - * @param $info - * The structured information array. Look at _draggableviews_info(..) to learn more. - * - */ -function _draggableviews_check_order(&$info) { - foreach ($info['nodes'] as $nid => $prop) { - _draggableviews_check_node_order($nid, $info); - - if ($prop['depth'] > 0) { - // Set the parent node order of the current level to 0 (B in the example). This should prevent - // broken structures caused by "bad nodes" (A in the example) with the same order at the parents level. - // B (0 -1) The parent of C - // A (0 -1) A bad node - // --C (0 1) The child node of B - // We take advantage of this when we sort the nodes. - $parent = $prop['parent']; - $depth = $prop['depth']; - $info['nodes'][$parent]['order'][$depth] = 0; - } - } -} - -/** - * Check Node Order Value - * - * We rely on the correctness of depth values and parent values. - * The order values of each level have to equal with the values of the parent nodes. - * - * @param $nid - * The node id to check. - * @param $info - * The structured information array. Look at _draggableviews_info(..) to learn more. - * - */ -function _draggableviews_check_node_order($nid, &$info) { - $error = FALSE; - $nodes = &$info['nodes']; - - $temp_nid = $nodes[$nid]['parent']; - - for ($i = $nodes[$nid]['depth'] - 1; $i >= 0; $i--) { - // We're operating top-down, so we determine the parents nid by the way. - $nodes[$nid]['order'][$i] = $nodes[$temp_nid]['order'][$i]; - - $temp_nid = $nodes[$temp_nid]['parent']; - } -} - -/** - * Reload Info Array - * - * @param $info - * The structured info array. Look at _draggableviews_info(..) to learn more. - * @param $items_per_page - * @param $current_page - */ -function _draggableviews_reload_info(&$info, $items_per_page = NULL, $current_page = NULL, $offset = NULL) { - // Re-execute the view because order values may have changed. - _draggableviews_re_execute_view($info['view'], $items_per_page, $current_page, $offset); - // Reload nodes of the info array. - $info = _draggableviews_info($info['view'], $info); -} - -/** - * Re-execute View - * - * @param $view - * The view object. - * @param $items_per_page - * @param $current_page - * @param $offset - */ -function _draggableviews_re_execute_view(&$view, $items_per_page = NULL, $current_page = NULL, $offset = NULL) { - if (isset($items_per_page)) { - $view->pager['items_per_page'] = $items_per_page; - } - if (isset($current_page)) { - $view->pager['current_page'] = $current_page; - // Views pager uses global variables where all already known information is dumped in. - // We need to change the global variable $pager_page_array in order to set the page to 0 because - // this variable would force the current page to another value. (see views/includes/view.inc:#717, function execute()) - global $pager_page_array; - $pager_page_array[$view->pager['element']] = $current_page; - } - if (isset($offset)) $view->pager['offset'] = $offset; - - $view->executed = FALSE; - $view->execute(); -} - -/** - * Get Handler Instance - * - * @param $field - * The field options specified in the style plugin. - * @param $view - * The view object. - * - * @return - * A handler instance. - */ -function _draggableviews_init_handler($field, &$view) { - if (isset($field['handler'])) { - $handler_info = draggableviews_discover_handlers($field['handler']); - $file = $handler_info['path'] .'/'. $handler_info['file']; - if ($handler_info['path'] && $handler_info['file'] && file_exists($file)) { - require_once($file); - $handler = new $handler_info['handler']; - $handler->init($field['field'], $view); - return $handler; - } - else { - return FALSE; - } - } - else { - return FALSE; - } -} - -/** - * Sort Nodes - * - * @param $nodes - * The nodes array to sort. - */ -function _draggableviews_sort_nodes(&$nodes) { - uasort($nodes, '_draggableviews_compare_nodes'); -} - -/** - * Compare Nodes (used by uasort) - * - * - * @param $node1 - * @param $node2 - * - * @return - * < or > or == - */ -function _draggableviews_compare_nodes($node1, $node2) { - // The first difference will be significant. If they equal in each level we - // "survive" the loop and end up with return 0. - for ($i = 0; $i < count($node1['order']); $i++) { - // First compare the order values.. - if ($node1['order'][$i] < $node2['order'][$i]) return -1; - if ($node1['order'][$i] > $node2['order'][$i]) return 1; - } - return 0; -} - + view, + * + * 'order' => array( + * 'field' => array( + * 'field_name' => field_name, + * 'field_alias' => field_alias, + * 'handler' => handler, + * ), + * 'visible' => TRUE/FALSE, + * ), + * + * 'hierarchy' => array( + * 'field' => array( + * 'field_name' => field_name, + * 'field_alias' => field_alias, + * 'handler' => handler, + * ), + * 'visible' => TRUE/FALSE, + * ), + * + * 'depth_limit' => depth_limit, + * + * 'types' = array( + * node_type1 => "root"/"leaf", + * node_type2 => "root"/"leaf", + * .. + * ), + * + * 'expand_links' = array( + * 'show' => TRUE/FALSE, + * 'default_collapsed' => TRUE/FALSE, + * 'by_uid' => TRUE/FALSE, + * ), + * + * 'view_window_extensions' = array( + * 'extension_top' => 3, + * 'extension_bottom' => 3, + * ), + * + * 'locked' => TRUE/FALSE, + * + * 'depth' => 0, + * + * 'default_on_top' => TRUE/FALSE, + * + * 'nodes' => array( + * nid1 => array( + * 'order' => array( + * 0 => value, + * 1 => value, + * .. + * ), + * 'parent' => value, + * ), + * .. + * ), + * ); + */ +function _draggableviews_info($view, $info = NULL) { + $options = $view->style_plugin->options; + $fields = $view->field; + $results = $view->result; + + // if there is already an info array just rebuild the nodes array and skip this section + if (!isset($info)) { + $info = array(); + + // extract draggableviews settings. + if ($options['tabledrag_order']['field'] != 'none') { + if ($handler = _draggableviews_init_handler($options['tabledrag_order'], $view)) { + $info['order'] = array( + 'field' => array( + 'handler' => $handler, + 'field_name' => $options['tabledrag_order']['field'], + 'field_alias' => $fields[$options['tabledrag_order']['field']]->field_alias, + ), + 'visible' => strcmp($options['tabledrag_order_visible']['visible'], 'visible') == 0 ? TRUE : FALSE, + ); + } + else { + drupal_set_message(t('Draggableviews: Handler ') . $options['tabledrag_order']['handler'] . t(' could not be found.'), 'error'); + unset($info['order']); + return $info; + } + $info['order']['visible'] = strcmp($options['tabledrag_order_visible']['visible'], 'visible') == 0 ? TRUE : FALSE; + + if ($options['tabledrag_hierarchy']['field'] != 'none') { + if ($handler = _draggableviews_init_handler($options['tabledrag_hierarchy'], $view)) { + $info['hierarchy'] = array( + 'field' => array( + 'handler' => $handler, + 'field_name' => $options['tabledrag_hierarchy']['field'], + 'field_alias' => $fields[$options['tabledrag_hierarchy']['field']]->field_alias, + ), + 'visible' => strcmp($options['tabledrag_hierarchy_visible']['visible'], 'visible') == 0 ? TRUE : FALSE, + ); + + $info['depth_limit'] = $options['draggableviews_depth_limit']; + if (!isset($info['depth_limit'])) $info['depth_limit'] = 10; + + if (isset($options['tabledrag_types'])) { + foreach ($options['tabledrag_types'] as $type) { + $info['types'][$type['node_type']] = $type['type']; + } + } + + $info['expand_links'] = array( + 'show' => strcmp($options['tabledrag_expand']['expand_links'], 'expand_links') == 0 ? TRUE : FALSE, + 'default_collapsed' => strcmp($options['tabledrag_expand']['collapsed'], 'collapsed') == 0 ? TRUE : FALSE, + 'by_uid' => strcmp($options['tabledrag_expand']['by_uid'], 'by_uid') == 0 ? TRUE : FALSE, + ); + } + else { + drupal_set_message(t('Draggableviews: Handler ') . $options['tabledrag_hierarchy']['handler'] . t(' could not be found.'), 'error'); + } + } + + $info['view_window_extensions'] = $options['draggableviews_extensions']; + if (!isset($info['view_window_extensions']['extension_top'])) $info['view_window_extensions']['extension_top'] = 3; + if (!isset($info['view_window_extensions']['extension_bottom'])) $info['view_window_extensions']['extension_bottom'] = 3; + + $info['locked'] = strcmp($options['tabledrag_lock']['lock'], 'lock') == 0 ? TRUE : FALSE; + } + } + + $info['depth'] = 0; + + $info['default_on_top'] = $options['draggableviews_default_on_top'] == 1 ? TRUE : FALSE; + + // Hold a reference of the view object itself. + $info['view'] = &$view; + + // Get all nodes and their properties. + $info['nodes'] = array(); + if (isset($info['order']) && isset($results) && count($results) > 0) { + // loop through all resulting nodes + foreach ($results as $row) { + if (is_numeric($row->{$info['order']['field']['field_alias']})) { + $info['nodes'][$row->nid]['order'][0] = (int)($row->{$info['order']['field']['field_alias']}); + } + else { + // Default position of new nodes. + $info['nodes'][$row->nid]['order'][0] = $info['default_on_top'] == 1 ? -1 : $view->total_rows; + } + if (isset($info['hierarchy'])) { + $info['nodes'][$row->nid]['parent'] = (int)($row->{$info['hierarchy']['field']['field_alias']}); + } + } + } + + return $info; +} + +/* + * Quick Check Structure + * + * I used the word "Quick" because only visible nodes and only the very top hierarchy level will be checked. If the + * return is TRUE we can be sure that the structure will be displayed correctly. But errors that don't affect the + * current output can not be detected. Enhanced checks will be done when we have to rebuild a broken structure. + * + * We check for the following: + * - Wrong order values: The order values must constantly increase by 1, independent of the hierarchy level. + * - Parent mismatch: The parent_nid must equal with the nid we memorized before we entered the current hierarchy level. + * + * @param $inputs + * The structured info array. Look at _draggableviews_info(..) to learn more. + * + * return + * TRUE if structure is valid + */ +function _draggableviews_quick_check_structure($info) { + // Calculate views page offset. + $pager = $info['view']->pager; + $offset = $pager['items_per_page'] * $pager['current_page'] + $pager['offset']; + + // Call function in checking mode (renumber = FALSE). The order value must begin with $offset. + return _draggableviews_ascending_numbers($info, $offset, FALSE); +} + +/** + * Build hierarchy + * + * Although there shouldn't be any structure based errors after submit + * broken parents can be detected and repaired. All other structure based errors + * can not be detected. If there are any they will be detected by _draggableviews_quick_check_structure(..) + * and finally repaired by _draggableviews_analyze_structure(..). + * + * @param $info + * The structured information array + */ +function _draggableviews_build_hierarchy(&$info) { + $nodes = &$info['nodes']; + $input = &$info['input']; + + foreach ($nodes as $nid => $prop) { + // get depth + if (($depth = _draggableviews_get_hierarchy_depth($nid, $input, $info)) === FALSE) { + // Error! The hierarchy structure is broken and could + // look like the following: (we're currently processing X) + // A + // --X + // --D + // + // The next steps: + // 1) bring it down to the root level + // 2) Set order fields to the minimum + + $input[$nid]['parent'] = 0; + + // We gracefully sidestep the order-loop + $depth = -1; + } + + // Let's take a look at the following expample, to understand + // what is beeing done. + // + // A + // --B + // --C + // --X + // --D + // E + // Imagine we're currently processing X: + // + // We know that X is in depth=3, so we save the received + // weight value in the 3rd order field of node X. + // + // The 2nd order field must inherit the received weight of + // node C (the next parent). And the 1st order field must + // inherit the received weight of node A (the parent of C). + // + // When we finally order the view by weight1, weight2, weight3 then + // weight1 and weight2 from node X will always equal with + // those from node A and B, and weight3 defines the order of the 3rd level. + + $temp_nid = $nid; + + for ($i = $depth; $i >= 0; $i--) { + // we're operating top-down, so we determine the parents nid by the way + + $nodes[$nid]['order'][$i] = $input[$temp_nid]['order'][0]; + + if (isset($info['hierarchy']) && $i > 0) { + if (!($temp_nid = $input[$temp_nid]['parent'])) { + // this line should never be reached assumed the depth + // was calculated correctly. + drupal_set_message(t('Undefined State called in draggableviews_build_hierarchy(..)'), 'error'); + break; + } + } + } + + if (isset($info['hierarchy'])) { + // Simply set the parent value + $nodes[$nid]['parent'] = $input[$nid]['parent']; + } + + // Now set the next level to the minimum value. Otherwise + // it could happen that a child appears above its parent. + // The ? can be anything, unfortunately also > 5 + // + // --A (3,5) + // B (3,?) + // + // To guaranteer that the ? is always the lowest, we choose + // the minimum. + + $depth = ($depth == -1) ? 0 : $depth; + + $nodes[$nid]['order'][$depth + 1] = $info['order']['field']['handler']->get_minimum_value(); + $nodes[$nid]['depth'] = $depth; + } + + // Last but not least sort nodes and finally assign ascending numbers. This is necessary since this module supports paging. + _draggableviews_sort_nodes($nodes); + // calculate views page offset + $pager = $info['view']->pager; + $offset = $pager['items_per_page'] * $pager['current_page'] + $pager['offset']; + _draggableviews_ascending_numbers($info, $offset, TRUE); +} + +/** + * Rebuild hierarchy + * + * This function is called when the structure is broken. + * + * @param $info + * The structured information array. Look at _draggableviews_info(..) to learn more. + */ +function _draggableviews_rebuild_hierarchy(&$info) { + // We backup the page settings and restore them after completing all operations. + $backup_pager = $info['view']->pager; + + if ($backup_pager['items_per_page'] > 0) { + // We have to make sure that there's no hidden node with an order value that refers to the current page. + // If the items to display per page are limitated we load the entire view. + + //@todo: We reduce the fields to a minimum because of performance issues. + + _draggableviews_reload_info($info, 0, 0, 0); + } + + // Calculate depth values. + // Nodes with broken parents will be brought down to the root level. + // These depth values will be used for both theming and repairing broken structures. + _draggableviews_calculate_depths($info); + + // Detect and repair ordering errors. + // The nodes order values have to equal with the parents order values. + _draggableviews_check_order($info); + + // The last issue we have to deal with is the order itself. Probably there + // are more many with the same order value what could lead to display errors. + // We just sort the nodes by their current order values and subsequent we assign ascending numbers. + _draggableviews_sort_nodes($info['nodes']); + _draggableviews_ascending_numbers($info, 0, TRUE); + + // Save hierarchy. + _draggableviews_save_hierarchy($info); + + // The structure should be valid now. + // Nonetheless let's make a final check for debugging reasons. + if (!_draggableviews_quick_check_structure($info)) { + drupal_set_message("Draggableviews: Rebuilding structure didn't work. The structure is broken.", "error"); + } +} + +/** + * (CHECK/WRITE) Ascending Numbers. + * + * This function is used for both renumbering and checking. + * Order values have to start with $offset. + * + * @param $info + * The structured info array. Look at _draggableviews_info(..) to learn more. + * @param $offset + * Where we start to count. + * @param $renumber + * Renumber or check. + * + * @return + * TRUE if no errors found. + */ +function _draggableviews_ascending_numbers(&$info, $offset = 0, $renumber = FALSE) { + // We need to hold + // 1) the last nid, + // 2) the last order value, + // 3) the parent's nid of each hierarchy level, + // 3a) the current depth (hierarchy level), + $last_nid = -1; + $last_order = $offset; + $last_parent_nid = array(0); + $depth = 0; + + foreach ($info['nodes'] as $nid => $values) { + $parent_nid = isset($info['hierarchy']) ? $values['parent'] : 0; + + // Let's take a look at the following expample, to understand what is beeing done. + // The order value of the parent's level always equals with the parent's value. + // + // A (0 - -) First node of level0. + // --B (0 1 -) First node of level1. But we continue counting. + // --C (0 1 2) First node of level2. But we still continue counting. + // X (3 - -) Now we leave 2 levels (level2->level0). We still have to continue counting. + // + // Imagine we are currently processing X. We are leaving 2 levels and we need to determine the + // last order value that was used. First we check how many levels are beeing left: + $leave_hierarchy_levels = 0; + for ($i = 0; $i < $depth; $i++) { + if ($parent_nid == $last_parent_nid[$i]) { + // Found the level we go to. Calculate the number of levels that are beeing left. + $leave_hierarchy_levels = $depth - $i; + break; + } + } + if ($leave_hierarchy_levels > 0) { + // We leave some hierarchy levels. We simply inherit the order value of the level we come from. + $depth -= $leave_hierarchy_levels; + // Remove obsoleted values from stack. + for ($i = 0; $i < $leave_hierarchy_levels; $i++) { + array_pop($last_parent_nid); + } + } + else if ($parent_nid == $last_nid) { + // We are entering a new level. This is the first child of the last node. + array_push($last_parent_nid, $last_nid); + $depth++; + } + else if ($parent_nid != $last_parent_nid[$depth]) { + // This node is neither a member of any previous hierarchy level + // nor a child of the last node (opening a new level) + // nor a member of the current hierarchy level + // There's something wrong! + if ($renumber) { + drupal_set_message("Something wrong in _draggableviews_ascending_numbers (". ($renumber ? 'WRITE' : 'CHECK') .").", "error"); + } + return FALSE; + } + + // This function is used for both renumbering and checking the hierarchy. Decide what we have to do: + if ($renumber) { + // Assign order value. + $info['nodes'][$nid]['order'][0] = $last_order; + } + else { + // Check structure. + $order = $values['order'][0]; + if ($order != $last_order) { + // This would cause troubles with paging. + // We better initiate a rebuild! + return FALSE; + } + } + + $last_nid = $nid; + $last_order++; + } + + return TRUE; +} + +/** + * Extend View Window + * + * In order to make it possible to drag from one page to another we have to extend the visible window of each page. + * We also have to make sure that parent nodes appear with all their children (..and child nodes with their parent). + * The view will be re-executed with the calculated range, based on the suggested range. + * + * @param $info + * The structured info array. Look at _draggableviews_info(..) to learn more. + * @param $extension_top + * @param $extension_bottom + * + * @return + * An array containing the new, calculated range. + */ +function _draggableviews_extend_view_window(&$info, $reload = FALSE) { + // Check page settings. + if ($info['pager']['items_per_page'] <= 0) { + // Nothing to do. Just return the actual range. + return array( + 'first_index' => 0, + 'last_index' => count($info['view']->result) - 1, + ); + } + + // Notice that we have two sets of pager information: + // - The "should-be" values at $info['pager'] + // - The real, current values at $view->pager. + // For building the new view we always use the "should-be" pager information. + + $items_per_page = $info['pager']['items_per_page']; + $current_page = $info['pager']['current_page']; + $offset = $info['pager']['offset']; + + $first_index = $items_per_page * $current_page; + $last_index = $items_per_page * ($current_page + 1) - 1; + + $nodes = $info['nodes']; + + // If the new index is out of range we'll use the last existing index. Calculate this index: + $current_first_index = $info['view']->pager['items_per_page'] * $info['view']->pager['current_page'] + $info['view']->pager['offset']; + $total_index = $current_first_index + count($nodes) - 1; + + if ($first_index > $total_index) { + // $first_index is out of range. + $first_index = 0; + $last_index = $items_per_page -1; + } + + // Check permissions and lock. + if (!user_access('Allow Reordering') || $info['locked']) { + // Don't extend view window (= suggestion). + $extension_top = 0; + $extension_bottom = 0; + } + else { + $extension_top = $info['view_window_extensions']['extension_top']; + $extension_bottom = $info['view_window_extensions']['extension_bottom']; + } + + // Extend on top: + $first_index -= $extension_top; + if ($first_index < 0) { + // Be careful: $first_index < 0. So this is a subtraction! + $extension_top += $first_index; + if ($extension_top < 0) $extension_top = 0; + $first_index = 0; + } + // Extend on bottom: + $last_index += $extension_bottom; + if ($last_index > $total_index) { + $extension_bottom = $extension_bottom - ($last_index - $total_index); + if ($extension_bottom < 0) $extension_bottom = 0; + $last_index = $total_index; + } + + $add_to_extension_top = 0; + $add_to_extension_bottom = 0; + + if (isset($info['hierarchy'])) { + // There are possibly some child nodes that would be cut of their parents. To avoid this we localize + // the next parents (if needed) and use their index for the range. To analyze the nodes array we slice out + // all nodes before (the B's) and all nodes after (the a's) the current page (the -'s): + // + // B B B B B B B B B - - - - - - - - A A A A A A A A A A. + // + // Slice out all nodes before (currently not shown): + $nodes_before = array_slice($nodes, 0, $first_index, TRUE); + // Slice out all nodes after (currently not shown): + // array_slice: In PHP version < 5.0.2 the 3rd argument (length) must not be ommited (set to NULL). The value must be the exact amount of elements. + $nodes_after = array_slice($nodes, $last_index + 1, strlen($nodes) + $last_index, TRUE); + // Slice out all nodes that are currently shown: + $nodes_current = array_slice($nodes, $first_index, $last_index - $first_index + 1, TRUE); + + $first_node = current($nodes_current); + if ($first_node['parent'] > 0) { + // The first node of the current page is a child node. Look for the next parent + // that is on the root level. We can't use foreach() because we iterate the wrong way. + $temp_node = end($nodes_before); + while ($temp_node !== FALSE) { + $add_to_extension_top++; + if ($temp_node['parent'] == 0) break; + $temp_node = prev($nodes_before); + } + } + // Check for possible children of the last node. + foreach ($nodes_after AS $temp_node) { + if ($temp_node['parent'] == 0) break; + $add_to_extension_bottom++; + } + + // Update range. + $first_index -= $add_to_extension_top; + $last_index += $add_to_extension_bottom; + } + + if ($reload) { + // Re-execute the view and reload the info array. We need to add 1 to the number of items + // to show (e.g. show from index 5 to index 5: show 5-5+1=1 item). + _draggableviews_reload_info($info, $last_index - $first_index + 1, 0, $offset + $first_index); + } + + // Mark extended nodes as "extension". + $nodes = &$info['nodes']; + for ($i = 0; $i < $extension_top + $add_to_extension_top; $i++) { + $nid = key($nodes); + $nodes[$nid]['extension'] = TRUE; + next($nodes); + } + end($nodes); + for ($i = 0; $i < $extension_bottom + $add_to_extension_bottom; $i++) { + $nid = key($nodes); + $nodes[$nid]['extension'] = TRUE; + prev($nodes); + } + + return array( + 'first_index' => $first_index, + 'last_index' => $last_index, + ); +} + +/** + * Click Sort + * + * The nodes are already in the right order. + * Simply assign ascending values. + * Re-execute view and refresh info array. + * + * @param $info + * The structured info array. Look at _draggableviews_info(..) to learn more. + */ +function _draggableviews_click_sort(&$info) { + // Check permissions. + if (!user_access('Allow Reordering')) { + drupal_set_message(t('You are not allowed to reorder nodes.'), 'error'); + return; + } + + // Check if structure is locked. + if ($info['locked']) { + drupal_set_message(t('The structure is locked.'), 'error'); + return; + } + + // If the items to display per page are limited we load the entire view. + $pager = $info['view']->pager; + if ($pager['items_per_page'] > 0) { + _draggableviews_reload_info($info, 0, 0, 0); + } + + // First bring all child nodes down to the root level. We can't keep the hierarchy information + // because the nodes have been mixed up thoroughly. + if (isset($info['hierarchy'])) { + foreach ($info['nodes'] AS $nid => $values) { + $info['nodes'][$nid]['parent'] = 0; + $info['nodes'][$nid]['depth'] = 0; + } + } + // Assign ascending numbers + _draggableviews_ascending_numbers($info, 0, TRUE); + _draggableviews_save_hierarchy($info); + + // Re-execute view with original page settings. + _draggableviews_reload_info($info, $pager['items_per_page'], $pager['current_page'], $pager['offset']); +} + +/** + * Save Hierarchy + * + * @param $info + * The structured information array. Look at _draggableviews_info(..) to learn more. + */ +function _draggableviews_save_hierarchy($info) { + // Loop through all nodes. + foreach ($info['nodes'] as $nid => $prop) { + if (isset($info['hierarchy'])) { + $value = $prop['parent']; + $handler = $info['hierarchy']['field']['handler']; + $handler->save($nid, $value); + } + + $value = $prop['order'][0]; + $handler = $info['order']['field']['handler']; + $handler->save($nid, $value); + } +} + +/** + * Calculate Depths + * + * Nodes with broken parents will be brought down to the root level. + * + * @param $info + * The structured information array. Look at _draggableviews_info(..) to learn more. + * + * @return + * TRUE if no errors occured. + */ +function _draggableviews_calculate_depths(&$info) { + $error = FALSE; + + // Loop through all rows the view returned. + foreach ($info['nodes'] as $nid => $prop) { + if ($info['nodes'][$nid]['parent'] < 0) $info['nodes'][$nid]['parent'] = 0; + + // Determine hierarchy depth of current row. + $depth = _draggableviews_get_hierarchy_depth($nid, $info['nodes'], $info); + + if ($depth === FALSE) { + $error = TRUE; + $depth = 0; + $info['nodes'][$nid]['parent'] = 0; + } + + $info['nodes'][$nid]['depth'] = $depth; + + // Move the order value to the calculated level. + $info['nodes'][$nid]['order'][$depth] = $info['nodes'][$nid]['order'][0]; + + // Set the next level to the minimum value. Otherwise + // it could happen that a child appears above its parent. + $info['nodes'][$nid]['order'][$depth + 1] = $info['order']['field']['handler']->get_minimum_value(); + + if ($depth > $info['depth']) $info['depth'] = $depth; + } + + return !$error; +} + +/** + * Get Hierarchy Depth + * + * This function detects broken parents. + * Cycles are detected (if a child is the parent of its parent). + * + * @param $nid + * The nid of the node which should be processed. + * @param $nodes + * The nodes array. This can be both a nodes array or an input array. + * They share the same hierarchy properties. + * @param $info + * The structured info array. Look at _draggableviews_info(..) to learn more. + * + * @return + * The hierarchy depth if no occured, else FALSE. + */ +function _draggableviews_get_hierarchy_depth($nid, $nodes, $info) { + if (!isset($info['hierarchy'])) return 0; + + $depth = 0; + $error = FALSE; + $temp_nid = $nid; + $used_nids = array($temp_nid); + + while (!($error = !isset($nodes[$temp_nid])) && ($temp_nid = $nodes[$temp_nid]['parent']) > 0) { + // In order to detect cycles we use an array, + // where all used nids are saved in. If a node id + // occurs twice -> return FALSE. + $cycle_found = array_search($temp_nid, $used_nids); + // The isset(..) is necessary because in PHP-Versions <= 4.2.0 array_search() returns NULL + // instead of FALSE if $temp_nid was not found. + if (isset($cycle_found) && $cycle_found !== FALSE) { + drupal_set_message(t("Draggableviews: A cycle was found.")); + return FALSE; + } + + $used_nids[] = $temp_nid; + $depth++; + } + + if ($error) { + // If loop breaked caused by an error. + return FALSE; + } + + return $depth; +} + +/** + * Detect and Repair Order Values + * + * The nodes order value has to equal with the parents order value. + * + * @param $info + * The structured information array. Look at _draggableviews_info(..) to learn more. + * + */ +function _draggableviews_check_order(&$info) { + foreach ($info['nodes'] as $nid => $prop) { + _draggableviews_check_node_order($nid, $info); + + if ($prop['depth'] > 0) { + // Set the parent node order of the current level to 0 (B in the example). This should prevent + // broken structures caused by "bad nodes" (A in the example) with the same order at the parents level. + // B (0 -1) The parent of C + // A (0 -1) A bad node + // --C (0 1) The child node of B + // We take advantage of this when we sort the nodes. + $parent = $prop['parent']; + $depth = $prop['depth']; + $info['nodes'][$parent]['order'][$depth] = 0; + } + } +} + +/** + * Check Node Order Value + * + * We rely on the correctness of depth values and parent values. + * The order values of each level have to equal with the values of the parent nodes. + * + * @param $nid + * The node id to check. + * @param $info + * The structured information array. Look at _draggableviews_info(..) to learn more. + * + */ +function _draggableviews_check_node_order($nid, &$info) { + $error = FALSE; + $nodes = &$info['nodes']; + + $temp_nid = $nodes[$nid]['parent']; + + for ($i = $nodes[$nid]['depth'] - 1; $i >= 0; $i--) { + // We're operating top-down, so we determine the parents nid by the way. + $nodes[$nid]['order'][$i] = $nodes[$temp_nid]['order'][$i]; + + $temp_nid = $nodes[$temp_nid]['parent']; + } +} + +/** + * Reload Info Array + * + * @param $info + * The structured info array. Look at _draggableviews_info(..) to learn more. + * @param $items_per_page + * @param $current_page + */ +function _draggableviews_reload_info(&$info, $items_per_page = NULL, $current_page = NULL, $offset = NULL) { + // Re-execute the view because order values may have changed. + _draggableviews_re_execute_view($info['view'], $items_per_page, $current_page, $offset); + // Reload nodes of the info array. + $info = _draggableviews_info($info['view'], $info); +} + +/** + * Re-execute View + * + * @param $view + * The view object. + * @param $items_per_page + * @param $current_page + * @param $offset + */ +function _draggableviews_re_execute_view(&$view, $items_per_page = NULL, $current_page = NULL, $offset = NULL) { + if (isset($items_per_page)) { + $view->pager['items_per_page'] = $items_per_page; + } + if (isset($current_page)) { + $view->pager['current_page'] = $current_page; + // Views pager uses global variables where all already known information is dumped in. + // We need to change the global variable $pager_page_array in order to set the page to 0 because + // this variable would force the current page to another value. (see views/includes/view.inc:#717, function execute()) + global $pager_page_array; + $pager_page_array[$view->pager['element']] = $current_page; + } + if (isset($offset)) $view->pager['offset'] = $offset; + + $view->executed = FALSE; + $view->execute(); +} + +/** + * Get Handler Instance + * + * @param $field + * The field options specified in the style plugin. + * @param $view + * The view object. + * + * @return + * A handler instance. + */ +function _draggableviews_init_handler($field, &$view) { + if (isset($field['handler'])) { + $handler_info = draggableviews_discover_handlers($field['handler']); + $file = $handler_info['path'] .'/'. $handler_info['file']; + if ($handler_info['path'] && $handler_info['file'] && file_exists($file)) { + require_once($file); + $handler = new $handler_info['handler']; + $handler->init($field['field'], $view); + return $handler; + } + else { + return FALSE; + } + } + else { + return FALSE; + } +} + +/** + * Sort Nodes + * + * @param $nodes + * The nodes array to sort. + */ +function _draggableviews_sort_nodes(&$nodes) { + uasort($nodes, '_draggableviews_compare_nodes'); +} + +/** + * Compare Nodes (used by uasort) + * + * + * @param $node1 + * @param $node2 + * + * @return + * < or > or == + */ +function _draggableviews_compare_nodes($node1, $node2) { + // The first difference will be significant. If they equal in each level we + // "survive" the loop and end up with return 0. + for ($i = 0; $i < count($node1['order']); $i++) { + // First compare the order values.. + if ($node1['order'][$i] < $node2['order'][$i]) return -1; + if ($node1['order'][$i] > $node2['order'][$i]) return 1; + } + return 0; +} + Index: draggableviews.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews.info,v retrieving revision 1.7.2.6 diff -u -p -r1.7.2.6 draggableviews.info --- draggableviews.info 25 Apr 2009 12:47:21 -0000 1.7.2.6 +++ draggableviews.info 3 Jun 2009 17:20:49 -0000 @@ -1,13 +1,13 @@ -; $Id: draggableviews.info,v 1.7.2.6 2009/04/25 12:47:21 sevi Exp $ -name = DraggableViews -description = Makes Views draggable -dependencies[] = views -package = Views -core = 6.x - -; Information added by drupal.org packaging script on 2008-09-21 -version = "6.x-3.0" -core = "6.x" -project = "draggableviews" -datestamp = "1222032618" - +; $Id: draggableviews.info,v 1.7.2.6 2009/04/25 12:47:21 sevi Exp $ +name = DraggableViews +description = Makes Views draggable +dependencies[] = views +package = Views +core = 6.x + +; Information added by drupal.org packaging script on 2008-09-21 +version = "6.x-3.0" +core = "6.x" +project = "draggableviews" +datestamp = "1222032618" + Index: draggableviews.js =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews.js,v retrieving revision 1.3.2.7 diff -u -p -r1.3.2.7 draggableviews.js --- draggableviews.js 8 May 2009 12:40:40 -0000 1.3.2.7 +++ draggableviews.js 3 Jun 2009 17:20:49 -0000 @@ -1,149 +1,149 @@ -Drupal.behaviors.draggableviewsLoad = function() { - $("table.views-table").each( function(i) { - var table_id = $(this).attr('id'); - - if (!Drupal.settings.draggableviews[table_id]) return; - - $(this).find("tr.draggable").each( function(i) { - var nid = $(this).find('td > .hidden_nid').attr('value'); - // append icon only if we find at least one child - if ($("#" + table_id + " tr:has(td > ." + Drupal.settings.draggableviews[table_id].parent + "[value=" + nid + "])").size() > 0) { - $(this).find('td:first').each( function(i) { - $(this).append('
').children('.draggableviews-expand').bind('click', function(){draggableviews_collapse(nid, table_id);}); - }); - } - - // Apply collapsed/expanded state. - if (Drupal.settings.draggableviews[table_id]) { - if (Drupal.settings.draggableviews[table_id].states) { - if (Drupal.settings.draggableviews[table_id].states[nid]) { - // When list should be collapsed.. - if (Drupal.settings.draggableviews[table_id].states[nid] == 1) { - // ..collapse list. - draggableviews_collapse(nid, table_id); - - // ..and set hidden field. - draggableviews_set_state_field(nid, table_id, true); - } - } - } - } - }); - - // collapse all by default if set - if( Drupal.settings.draggableviews.expand_default && Drupal.settings.draggableviews[table_id].expand_default == 1 ) { - draggableviews_collapse_all(table_id); - } - }); -} - -// Expand recursively. -function draggableviews_expand(parent_id, table_id, force){ - if (force || draggableviews_get_state_field(parent_id, table_id)) { - // show elements - draggableviews_show(parent_id, table_id); - - // swap link to collapse link - $("#" + table_id + " tr:has(td .hidden_nid[value="+parent_id+"])") - .find('.draggableviews-collapse').each( function (i){ - $(this).unbind('click'); - $(this).attr('class', 'draggableviews-expand'); - $(this).bind('click', function(){ draggableviews_collapse(parent_id, table_id); }); - // set state as value of a hidden field - draggableviews_set_state_field(parent_id, table_id, false); - }); - } -} - -// show recursively -function draggableviews_show(parent_id, table_id) { - $("table[id='" + table_id + "'] tr:has(td ." + Drupal.settings.draggableviews[table_id].parent + "[value="+parent_id+"])").each( function (i) { - $(this).show(); - var nid = $(this).find('td .hidden_nid').attr('value'); - if (nid) { - draggableviews_expand(nid, table_id, false); - } - }); -} - -function draggableviews_collapse(parent_id, table_id) { - // hide elements - draggableviews_hide(parent_id, table_id); - - // swap link to expand link - $("#" + table_id + " tr:has(td .hidden_nid[value=" + parent_id + "])") - .find('.draggableviews-expand').each( function (i){ - $(this).unbind('click'); - $(this).attr('class', 'draggableviews-collapse'); - $(this).bind('click', function(){ draggableviews_expand(parent_id, table_id, true); }); - - // set state as value of a hidden field - draggableviews_set_state_field(parent_id, table_id, true); - }); -} - -// hide recursively -function draggableviews_hide(parent_id, table_id) { - $("#" + table_id + " tr:has(td ." + Drupal.settings.draggableviews[table_id].parent + "[value=" + parent_id+"])").each( function (i) { - $(this).hide(); - var nid = $(this).find('td .hidden_nid').attr('value'); - if (nid) { - draggableviews_hide(nid, table_id, false); - } - }); -} - -function draggableviews_collapse_all(table_id) { - // hide elements - $("#" + table_id + " tr:has(td ." + Drupal.settings.draggableviews[table_id].parent + "[value<>0])").each( function (i) { - $(this).hide(); - }); - - // swap links to expand links - $("#" + table_id + " tr:has(td .hidden_nid)").each( function (i){ - var parent_id = $(this).find('td .hidden_nid').attr('value'); - - $(this).find('.draggableviews-expand').each( function (i){ - // set new action and class - $(this).unbind('click'); - $(this).attr('class', 'draggableviews-collapse'); - $(this).bind('click', function() { draggableviews_expand(parent_id, table_id); }); - - // set collapsed/expanded state - draggableviews_set_state_field(parent_id, table_id, true); - }); - }); -} - -// save state of expanded/collapsed fields in a hidden field -function draggableviews_set_state_field(parent_id, table_id, state) { - //build field name - var field_name = 'draggableviews_collapsed_' + parent_id; - - $("table[id='" + table_id + "'] input[name='hidden_nid'][value='" + parent_id + "']") - .parent().each( function(i) { - var replaced = false; - - // "check" if field already exists (..by just selecting it) - $(this).find('input[name="' + field_name + '"]').each( function(i) { - $(this).attr('value', state ? '1' : '0'); - replaced = true; - }); - // if no field existed yet -> create it - if (replaced == false) { - $(this).append(''); - } - }); -} - -// Get state of expanded/collapsed field. -function draggableviews_get_state_field(parent_id, table_id) { - //build field name - var field_name = 'draggableviews_collapsed_' + parent_id; - - var value = $('table[id="' + table_id + '"] input[name="' + field_name + '"]').attr('value'); - - if (value == 1) return false; - - return true; +Drupal.behaviors.draggableviewsLoad = function() { + $("table.views-table").each( function(i) { + var table_id = $(this).attr('id'); + + if (!Drupal.settings.draggableviews[table_id]) return; + + $(this).find("tr.draggable").each( function(i) { + var nid = $(this).find('td > .hidden_nid').attr('value'); + // append icon only if we find at least one child + if ($("#" + table_id + " tr:has(td > ." + Drupal.settings.draggableviews[table_id].parent + "[value=" + nid + "])").size() > 0) { + $(this).find('td:first').each( function(i) { + $(this).append('
').children('.draggableviews-expand').bind('click', function(){draggableviews_collapse(nid, table_id);}); + }); + } + + // Apply collapsed/expanded state. + if (Drupal.settings.draggableviews[table_id]) { + if (Drupal.settings.draggableviews[table_id].states) { + if (Drupal.settings.draggableviews[table_id].states[nid]) { + // When list should be collapsed.. + if (Drupal.settings.draggableviews[table_id].states[nid] == 1) { + // ..collapse list. + draggableviews_collapse(nid, table_id); + + // ..and set hidden field. + draggableviews_set_state_field(nid, table_id, true); + } + } + } + } + }); + + // collapse all by default if set + if( Drupal.settings.draggableviews.expand_default && Drupal.settings.draggableviews[table_id].expand_default == 1 ) { + draggableviews_collapse_all(table_id); + } + }); +} + +// Expand recursively. +function draggableviews_expand(parent_id, table_id, force){ + if (force || draggableviews_get_state_field(parent_id, table_id)) { + // show elements + draggableviews_show(parent_id, table_id); + + // swap link to collapse link + $("#" + table_id + " tr:has(td .hidden_nid[value="+parent_id+"])") + .find('.draggableviews-collapse').each( function (i){ + $(this).unbind('click'); + $(this).attr('class', 'draggableviews-expand'); + $(this).bind('click', function(){ draggableviews_collapse(parent_id, table_id); }); + // set state as value of a hidden field + draggableviews_set_state_field(parent_id, table_id, false); + }); + } +} + +// show recursively +function draggableviews_show(parent_id, table_id) { + $("table[id='" + table_id + "'] tr:has(td ." + Drupal.settings.draggableviews[table_id].parent + "[value="+parent_id+"])").each( function (i) { + $(this).show(); + var nid = $(this).find('td .hidden_nid').attr('value'); + if (nid) { + draggableviews_expand(nid, table_id, false); + } + }); +} + +function draggableviews_collapse(parent_id, table_id) { + // hide elements + draggableviews_hide(parent_id, table_id); + + // swap link to expand link + $("#" + table_id + " tr:has(td .hidden_nid[value=" + parent_id + "])") + .find('.draggableviews-expand').each( function (i){ + $(this).unbind('click'); + $(this).attr('class', 'draggableviews-collapse'); + $(this).bind('click', function(){ draggableviews_expand(parent_id, table_id, true); }); + + // set state as value of a hidden field + draggableviews_set_state_field(parent_id, table_id, true); + }); +} + +// hide recursively +function draggableviews_hide(parent_id, table_id) { + $("#" + table_id + " tr:has(td ." + Drupal.settings.draggableviews[table_id].parent + "[value=" + parent_id+"])").each( function (i) { + $(this).hide(); + var nid = $(this).find('td .hidden_nid').attr('value'); + if (nid) { + draggableviews_hide(nid, table_id, false); + } + }); +} + +function draggableviews_collapse_all(table_id) { + // hide elements + $("#" + table_id + " tr:has(td ." + Drupal.settings.draggableviews[table_id].parent + "[value<>0])").each( function (i) { + $(this).hide(); + }); + + // swap links to expand links + $("#" + table_id + " tr:has(td .hidden_nid)").each( function (i){ + var parent_id = $(this).find('td .hidden_nid').attr('value'); + + $(this).find('.draggableviews-expand').each( function (i){ + // set new action and class + $(this).unbind('click'); + $(this).attr('class', 'draggableviews-collapse'); + $(this).bind('click', function() { draggableviews_expand(parent_id, table_id); }); + + // set collapsed/expanded state + draggableviews_set_state_field(parent_id, table_id, true); + }); + }); +} + +// save state of expanded/collapsed fields in a hidden field +function draggableviews_set_state_field(parent_id, table_id, state) { + //build field name + var field_name = 'draggableviews_collapsed_' + parent_id; + + $("table[id='" + table_id + "'] input[name='hidden_nid'][value='" + parent_id + "']") + .parent().each( function(i) { + var replaced = false; + + // "check" if field already exists (..by just selecting it) + $(this).find('input[name="' + field_name + '"]').each( function(i) { + $(this).attr('value', state ? '1' : '0'); + replaced = true; + }); + // if no field existed yet -> create it + if (replaced == false) { + $(this).append(''); + } + }); +} + +// Get state of expanded/collapsed field. +function draggableviews_get_state_field(parent_id, table_id) { + //build field name + var field_name = 'draggableviews_collapsed_' + parent_id; + + var value = $('table[id="' + table_id + '"] input[name="' + field_name + '"]').attr('value'); + + if (value == 1) return false; + + return true; } \ No newline at end of file Index: draggableviews.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews.module,v retrieving revision 1.6.2.18 diff -u -p -r1.6.2.18 draggableviews.module --- draggableviews.module 1 Apr 2009 08:22:52 -0000 1.6.2.18 +++ draggableviews.module 3 Jun 2009 17:20:49 -0000 @@ -1,38 +1,38 @@ -'. t("Makes views draggable.") .'

'; - break; - } - return $output; -} - -/** - * Implementing hook_admin - */ + * Help text for the path. + */ +function draggableviews_help($path, $arg) { + $output = ''; + switch ($path) { + case "admin/help#draggableviews": + $output = '

'. t("Makes views draggable.") .'

'; + break; + } + return $output; +} + +/** + * Implementing hook_admin + */ function draggableviews_admin() { $form['draggableviews_repaired_msg'] = array( '#type' => 'textfield', @@ -41,49 +41,49 @@ function draggableviews_admin() { '#size' => 50, '#description' => t('Everytime a broken structure has been repaired this message will be shown. Per definition the structure cannot be broken after it has been repaired. Leave blank if you don\'t want to show any message.'), '#required' => FALSE, - ); - - return system_settings_form($form); -} - -/** - * Implementing hook_menu - */ -function draggableviews_menu() { - $items = array(); - $items['admin/settings/draggableviews'] = array( - 'title' => 'DraggableViews', - 'description' => 'Configure global settings', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('draggableviews_admin'), - 'access arguments' => array('administer site configuration'), - 'type' => MENU_NORMAL_ITEM, - ); - return $items; -} - -/** - * Implementing hook_forms - */ -function draggableviews_forms() { - $args = func_get_args(); - $form_id = $args[0]; - if (strpos($form_id, "draggableviews_view_draggabletable_form") === 0) { - $forms[$form_id] = array( - 'callback' => 'draggableviews_view_draggabletable_form', - ); - } - return $forms; -} - -/** - * Build the form - */ -function draggableviews_view_draggabletable_form($form_state, $view) { - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); + ); + + return system_settings_form($form); +} + +/** + * Implementing hook_menu + */ +function draggableviews_menu() { + $items = array(); + $items['admin/settings/draggableviews'] = array( + 'title' => 'DraggableViews', + 'description' => 'Configure global settings', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('draggableviews_admin'), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_NORMAL_ITEM, + ); + return $items; +} + +/** + * Implementing hook_forms + */ +function draggableviews_forms() { + $args = func_get_args(); + $form_id = $args[0]; + if (strpos($form_id, "draggableviews_view_draggabletable_form") === 0) { + $forms[$form_id] = array( + 'callback' => 'draggableviews_view_draggabletable_form', + ); + } + return $forms; +} + +/** + * Build the form + */ +function draggableviews_view_draggabletable_form($form_state, $view) { + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); $form['#submit'][] = 'draggableviews_view_draggabletable_form_submit'; // Use pager information and all exposed input data to build the query. $query = 'page='. $_GET['page']; @@ -107,29 +107,29 @@ function draggableviews_view_draggableta } } } - $form['#redirect'] = array('path' => $_GET['q'], 'query' => $query); - return $form; -} - -/** - * Implement hook_theme - */ -function draggableviews_theme() { - $array = array(); - $array['draggableviews_ui_style_plugin_draggabletable'] = array( - 'arguments' => array('form' => NULL), - ); - - $array['draggableviews_view_draggabletable_form_new'] = array( - 'template' => 'draggableviews-view-draggabletable-form', - 'preprocess functions' => array( - 'template_preprocess_draggableviews_view_draggabletable_form', - ), - 'arguments' => array('form' => NULL), - ); - - // Register theme function for all views. - $views = views_get_all_views(); + $form['#redirect'] = array('path' => $_GET['q'], 'query' => $query); + return $form; +} + +/** + * Implement hook_theme + */ +function draggableviews_theme() { + $array = array(); + $array['draggableviews_ui_style_plugin_draggabletable'] = array( + 'arguments' => array('form' => NULL), + ); + + $array['draggableviews_view_draggabletable_form_new'] = array( + 'template' => 'draggableviews-view-draggabletable-form', + 'preprocess functions' => array( + 'template_preprocess_draggableviews_view_draggabletable_form', + ), + 'arguments' => array('form' => NULL), + ); + + // Register theme function for all views. + $views = views_get_all_views(); foreach ($views AS $view) { foreach (array_keys($view->display) AS $display_id) { $array['draggableviews_view_draggabletable_form_'. $view->vid .'_'. $display_id] = array( @@ -139,10 +139,10 @@ function draggableviews_theme() { ), 'arguments' => array('form' => NULL), ); - } - } - return $array; -} + } + } + return $array; +} /** * Implementing hook_views_pre_execute @@ -225,20 +225,20 @@ function draggableviews_views_pre_execut } } } - -/** - * Imlpementing hook_views_pre_render - * - * We distinguish between two cases A and B: - * A) Click sort was used: - * - Renumber the results as they are returned from the view. Simply use ascending numbers. (and re-execute the view). - * + +/** + * Imlpementing hook_views_pre_render + * + * We distinguish between two cases A and B: + * A) Click sort was used: + * - Renumber the results as they are returned from the view. Simply use ascending numbers. (and re-execute the view). + * * B) The view is just going to be rendered. - * - Check the structure. If it's broken: + * - Check the structure. If it's broken: * - - Repair the structure (and re-execute the view). * - * In both cases extend the visible window if paging is beeing used. - */ + * In both cases extend the visible window if paging is beeing used. + */ function draggableviews_views_pre_render(&$view) { if (!isset($view->draggableviews_info)) { // No info array has been appended. Nothing to do. @@ -257,14 +257,14 @@ function draggableviews_views_pre_render return; } } - + if ($_GET['order']) { // CASE A) Click sort was used. Assign order values manually. - + _draggableviews_click_sort($info); // The entire view is loaded at the moment. We try to extend the visible window with the suggested // values and reload the view. The calculated range will be returned. - $range = _draggableviews_extend_view_window($info, TRUE); + $range = _draggableviews_extend_view_window($info, TRUE); } else { // CASE B) Extend view and check structure. @@ -277,19 +277,19 @@ function draggableviews_views_pre_render // Extend views window, but don't reload view. $range = _draggableviews_extend_view_window($info, FALSE); } - - if (!_draggableviews_quick_check_structure($info)) { + + if (!_draggableviews_quick_check_structure($info)) { // The structure is broken and has to be repaired. We restore the original page settings now because // the current settings are based on a broken structure. $info['view']->pager = $info['pager']; - // Rebuild the view. + // Rebuild the view. _draggableviews_rebuild_hierarchy($info); // Try to extend the visible window with the suggested values. The calculated range will be returned. $range = _draggableviews_extend_view_window($info, TRUE); - if (strlen(variable_get('draggableviews_repaired_msg', 'The structure was broken. It has been repaired.')) > 0) { + if (strlen(variable_get('draggableviews_repaired_msg', 'The structure was broken. It has been repaired.')) > 0) { drupal_set_message(variable_get('draggableviews_repaired_msg', 'The structure was broken. It has been repaired.')); - } + } } } @@ -308,166 +308,166 @@ function draggableviews_views_pre_render _draggableviews_calculate_depths($info); // Finally we set the selectable options of the order field. - $info['order']['field']['handler']->set_range($range['first_index'], $range['last_index']); -} - -/** - * Implementing hook_submit - */ -function draggableviews_view_draggabletable_form_submit($vars) { - // Check permissions. - if (!user_access('Allow Reordering')) { - drupal_set_message(t('You are not allowed to reorder nodes.'), 'error'); - return; + $info['order']['field']['handler']->set_range($range['first_index'], $range['last_index']); +} + +/** + * Implementing hook_submit + */ +function draggableviews_view_draggabletable_form_submit($vars) { + // Check permissions. + if (!user_access('Allow Reordering')) { + drupal_set_message(t('You are not allowed to reorder nodes.'), 'error'); + return; } // Check if structure is locked. if ($info['locked']) { drupal_set_message(t('The structure is locked.'), 'error'); return; - } - - // Gather all needed information. - $view = $vars['#parameters'][2]->view; - $results = $view->result; - $input = $vars['submit']['#post']; - $info = $view->draggableviews_info; - - if (!isset($info['order'])) return; + } + + // Gather all needed information. + $view = $vars['#parameters'][2]->view; + $results = $view->result; + $input = $vars['submit']['#post']; + $info = $view->draggableviews_info; + + if (!isset($info['order'])) return; // Check if structure is locked. if ($info['locked']) { drupal_set_message(t('The structure is locked.'), 'error'); return; } - - // Loop through all resulting nodes. - foreach ($results AS $row) { - // set order values + + // Loop through all resulting nodes. + foreach ($results AS $row) { + // set order values if (isset($info['order']['field'])) { // The input array must have the same structure as the node array. - // E.g. because of _draggableviews_get_hierarchy_depth(..). - $info['input'][$row->nid]['order'][0] = $input[$info['order']['field']['field_name'] .'_'. $row->nid]; - } - // Set parent values. - if (isset($info['hierarchy'])) { - $info['input'][$row->nid]['parent'] = $input[$info['hierarchy']['field']['field_name'] .'_'. $row->nid]; - } - } - - _draggableviews_build_hierarchy($info); - - _draggableviews_save_hierarchy($info); - - if (isset($info['hierarchy'])) { - // save expanded/collapsed states. + // E.g. because of _draggableviews_get_hierarchy_depth(..). + $info['input'][$row->nid]['order'][0] = $input[$info['order']['field']['field_name'] .'_'. $row->nid]; + } + // Set parent values. + if (isset($info['hierarchy'])) { + $info['input'][$row->nid]['parent'] = $input[$info['hierarchy']['field']['field_name'] .'_'. $row->nid]; + } + } + + _draggableviews_build_hierarchy($info); + + _draggableviews_save_hierarchy($info); + + if (isset($info['hierarchy'])) { + // save expanded/collapsed states. global $user; - $uid = $info['expand_links']['by_uid'] ? $user->uid : 0; - - foreach ($vars['submit']['#post'] AS $key => $val) { - if (ereg('draggableviews_collapsed_', $key)) { - $parent_nid = drupal_substr($key, 25); - db_query("DELETE FROM {draggableviews_collapsed} - WHERE uid=%d AND parent_nid=%d AND vid=%d", - $uid, $parent_nid, $view->vid); - - db_query("INSERT INTO {draggableviews_collapsed} - (uid, vid, parent_nid, collapsed) VALUES (%d, %d, %d, %d)", - $uid, $view->vid, $parent_nid, $val); - } - } - } -} - -/** - * Discover All Implementations For Draggableviews + $uid = $info['expand_links']['by_uid'] ? $user->uid : 0; + + foreach ($vars['submit']['#post'] AS $key => $val) { + if (ereg('draggableviews_collapsed_', $key)) { + $parent_nid = drupal_substr($key, 25); + db_query("DELETE FROM {draggableviews_collapsed} + WHERE uid=%d AND parent_nid=%d AND vid=%d", + $uid, $parent_nid, $view->vid); + + db_query("INSERT INTO {draggableviews_collapsed} + (uid, vid, parent_nid, collapsed) VALUES (%d, %d, %d, %d)", + $uid, $view->vid, $parent_nid, $val); + } + } + } +} + +/** + * Discover All Implementations For Draggableviews * * @param $filter_handler * The handler to return. * * @return - * Either the entire array with all handlers or the specified handler entry. - */ + * Either the entire array with all handlers or the specified handler entry. + */ function draggableviews_discover_handlers($filter_handler = NULL) { - // @todo there's no cache functionality implemented yet. - $cache = array(); - // Get implementation definitions from all modules. - foreach (module_implements('draggableviews_handlers') as $module) { - $function = $module .'_draggableviews_handlers'; - $result = $function(); - if (!is_array($result)) { - continue; - } - - $path = drupal_get_path('module', $module); - - foreach ($result as $handler => $def) { - if (!isset($def['path'])) { - $def['path'] = $path; - } - if (!isset($def['file'])) { - $def['file'] = "$handler.inc"; - } - if (!isset($def['handler'])) { - $def['handler'] = 'draggableviews_handler_'. $handler; - } - // Merge the new data. - $cache[$handler] = $def; - } - } - - if (isset($filter_handler)) { - if (isset($cache[$filter_handler])) { - return $cache[$filter_handler]; - } - else { - return FALSE; - } - } - return $cache; -} - + // @todo there's no cache functionality implemented yet. + $cache = array(); + // Get implementation definitions from all modules. + foreach (module_implements('draggableviews_handlers') as $module) { + $function = $module .'_draggableviews_handlers'; + $result = $function(); + if (!is_array($result)) { + continue; + } + + $path = drupal_get_path('module', $module); + + foreach ($result as $handler => $def) { + if (!isset($def['path'])) { + $def['path'] = $path; + } + if (!isset($def['file'])) { + $def['file'] = "$handler.inc"; + } + if (!isset($def['handler'])) { + $def['handler'] = 'draggableviews_handler_'. $handler; + } + // Merge the new data. + $cache[$handler] = $def; + } + } + + if (isset($filter_handler)) { + if (isset($cache[$filter_handler])) { + return $cache[$filter_handler]; + } + else { + return FALSE; + } + } + return $cache; +} + /** * Get Handlers List - * + * * @return - * A list of all draggableviews handlers. - */ -function draggableviews_get_handlers_list() { - $handlers = draggableviews_discover_handlers(); - foreach ($handlers as $handler => $def) { - $list[$handler] = $def['title']; - } - return $list; -} - -/** - * Implementing hook_draggableviews_handlers - */ -function draggableviews_draggableviews_handlers() { - return array( - 'native' => array( - 'file' => 'implementations/draggableviews_handler_native.inc', - 'title' => t('Native'), - 'description' => 'Storage of structure done by draggableviews', - 'handler' => 'draggableviews_handler_native', - ), - ); -} - -/** - * Implementing hook_perm - */ -function draggableviews_perm() { - return array('Allow Reordering'); -} - -/** - * Impleneting hook_views_api - */ -function draggableviews_views_api() { - return array( - 'api' => 2.0, - 'path' => drupal_get_path('module', 'draggableviews') .'/views', - ); -} + * A list of all draggableviews handlers. + */ +function draggableviews_get_handlers_list() { + $handlers = draggableviews_discover_handlers(); + foreach ($handlers as $handler => $def) { + $list[$handler] = $def['title']; + } + return $list; +} + +/** + * Implementing hook_draggableviews_handlers + */ +function draggableviews_draggableviews_handlers() { + return array( + 'native' => array( + 'file' => 'implementations/draggableviews_handler_native.inc', + 'title' => t('Native'), + 'description' => 'Storage of structure done by draggableviews', + 'handler' => 'draggableviews_handler_native', + ), + ); +} + +/** + * Implementing hook_perm + */ +function draggableviews_perm() { + return array('Allow Reordering'); +} + +/** + * Impleneting hook_views_api + */ +function draggableviews_views_api() { + return array( + 'api' => 2.0, + 'path' => drupal_get_path('module', 'draggableviews') .'/views', + ); +} Index: draggableviews_theme.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/draggableviews_theme.inc,v retrieving revision 1.6.2.20 diff -u -p -r1.6.2.20 draggableviews_theme.inc --- draggableviews_theme.inc 2 May 2009 22:10:33 -0000 1.6.2.20 +++ draggableviews_theme.inc 3 Jun 2009 17:20:49 -0000 @@ -497,4 +497,4 @@ function _draggableviews_filter_fields($ } } return $available_fields; -} \ No newline at end of file +} Index: styles.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/styles.css,v retrieving revision 1.3.2.6 diff -u -p -r1.3.2.6 styles.css --- styles.css 15 Mar 2009 14:55:20 -0000 1.3.2.6 +++ styles.css 3 Jun 2009 17:20:49 -0000 @@ -1,4 +1,4 @@ -.draggableviews-expand{ +.draggableviews-expand{ margin-left: 3px; position: relative; top:3px; @@ -8,7 +8,7 @@ height: 20px; display: inline-block; - cursor: pointer; + cursor: pointer; } .draggableviews-collapse{ margin-left: 3px; Index: views/draggableviews.views.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/draggableviews/views/draggableviews.views.inc,v retrieving revision 1.1.2.4 diff -u -p -r1.1.2.4 draggableviews.views.inc --- views/draggableviews.views.inc 22 Mar 2009 13:58:50 -0000 1.1.2.4 +++ views/draggableviews.views.inc 3 Jun 2009 17:20:49 -0000 @@ -1,34 +1,34 @@ - 'draggableviews', // This just tells our themes are elsewhere. - - 'style' => array( + */ +function draggableviews_views_plugins() { + return array( + 'module' => 'draggableviews', // This just tells our themes are elsewhere. + + 'style' => array( 'draggabletable' => array( - 'path' => drupal_get_path('module', 'draggableviews') .'/views', - 'title' => t('Draggable Table'), - 'help' => t('Displays rows in a table and makes them draggable.'), - 'handler' => 'draggableviews_plugin_style_draggabletable', + 'path' => drupal_get_path('module', 'draggableviews') .'/views', + 'title' => t('Draggable Table'), + 'help' => t('Displays rows in a table and makes them draggable.'), + 'handler' => 'draggableviews_plugin_style_draggabletable', 'theme' => 'draggableviews_view_draggabletable', - 'theme file' => 'draggableviews_theme.inc', - 'uses row plugin' => TRUE, - 'uses options' => TRUE, - 'uses fields' => TRUE, + 'theme file' => 'draggableviews_theme.inc', + 'uses row plugin' => TRUE, + 'uses options' => TRUE, + 'uses fields' => TRUE, 'type' => 'normal', - 'parent' => 'table', - ), - ), - ); + 'parent' => 'table', + ), + ), + ); } /** @@ -45,6 +45,9 @@ function draggableviews_views_handlers() 'views_handler_field_draggableviews_structure' => array( 'parent' => 'views_handler_field', ), + 'views_handler_field_draggableviews_navigation' => array( + 'parent' => 'views_handler_field', + ), // sort handlers 'views_handler_sort_draggableviews_structure' => array( @@ -97,6 +100,18 @@ function draggableviews_views_data() { } } + foreach (array('next', 'previous') as $k=>$direction) { + $data['draggableviews_structure0'][$direction] = array( + 'title' => t("Navigation - $direction NID"), + 'real field' => 'value', + 'field' => array( + 'handler' => 'views_handler_field_draggableviews_navigation', + 'help' => t("Returns the NID of the $direction node in a Draggableviews sequence, if it exists."), + 'click sortable' => FALSE, + ), + ); + } + return $data; } @@ -104,10 +119,10 @@ function draggableviews_views_query_alte for ($i = 0; $i < 2; $i++) { if (isset($query->table_queue['draggableviews_structure'. $i])) { $query->table_queue['draggableviews_structure'. $i]['join']->extra[] = array( - 'field' => 'vid', - 'operator' => '=', - 'value' => is_numeric($view->vid) ? $view->vid : 0, - 'numeric' => TRUE, + 'field' => 'vid', + 'operator' => '=', + 'value' => is_numeric($view->vid) ? $view->vid : 0, + 'numeric' => TRUE, ); } }
>">