This is a great module, but there is a real problem in the core code (in both 4.6 and 4.7.

It is possible, based on our tests, that $position is not unique for elements in a nodequeue.

Therefore, when you run "front" or "back", this code executes:

function nodequeue_queue_front($queue, $position) {
  if ($position < 2 || $position > $queue->count)
    return;
  $entry = db_fetch_object(db_query("SELECT * FROM {nodequeue_nodes} WHERE qid=$queue->qid AND position = $position"));
  db_query("DELETE FROM {nodequeue_nodes} WHERE qid=$queue->qid AND position = $position");
  db_query("UPDATE {nodequeue_nodes} SET position=position + 1 WHERE position < $position");
  db_query("INSERT INTO {nodequeue_nodes} (qid, nid, position) VALUES ($queue->qid, $entry->nid, 1)");
}

The DELETE at the top of this statement wipes out any assigned nodes with the value $position. (Hence, you might accidentally delete half the nodes in your queue).

This function, and nodequeue_queue_back() should be rewritten to call:

function nodequeue_queue_front($queue, $nid)

Rather than $position, since $nid is unique.

Comments

merlinofchaos’s picture

Title: Major bug in code » Node Queue position duplication can cause accidental deletion of queue entries
Assigned: Unassigned » merlinofchaos
Priority: Critical » Normal

I don't consider this a 'critical' bug, though it's definitely an annoying bug. Also, I prefer titles that describe the problem.

position should not be duplicated for a given qid; if it is, then that is a bug and it should be fixed. The code doesn't use NID as the primary identifier because it doesn't know what the NID is in all cases.

If your tests indicate that position can be duplicated, do you know how? That's the area that needs to be investigated.

agentrickard’s picture

StatusFileSize
new18.31 KB

Sorry, I've been working on a tight deadline.

Your strategy is fair enough, though I think DELETEs are never appropriate when you really mean to UPDATE (too much risk of damage).

It may also be a problem that I'm running 4.6 code (I think) -- your releases aren't labelled within the code, so I can't give you a version number.

Attached is a snapshot of the nodequeue_nodes table from one of our test installs.

In some cases, $position is not set to a unique value. I'm trying to track down when.

merlinofchaos’s picture

In order to use an update I'd have to set the position to some temporary value to ensure no duplication. It may be a better solution, I'll have to run through it. When I was writing the module I had originally not meant to use delete but ended up with it because the other method used more queries.

merlinofchaos’s picture

Oh yes, that snapshot shows there is definitely a bug in there with respect to setting the position flag. I looked but I couldn't see a glaring error, but it is clearly there. I'll have to see if I can do it.

Do the various QIDs that have a bunch of 3s in the position table have anything in common?

agentrickard’s picture

StatusFileSize
new6.2 KB

I think the problem may exist when the Nodes in the NQ are Flexinodes, not regular Drupal nodes.

The errors went away when I reset the $position values for all my NQs. Then I assigned some stories, and all went as planned.

But when I assigned some "media" -- $node->type = 'flexinode-2' --, I got duplicate $position values whenever I used a function button.

See attached screenshot.

agentrickard’s picture

To be more clear, the rules for my NQ allow the assignment of Flexinodes to the Q, but when I reorder them, the $position value doesn't increment correctly.

I gotta run. Back tomorrow.

agentrickard’s picture

Status: Active » Needs review
StatusFileSize
new26.2 KB

OK. This took some testing, but I think I finally tracked it down.

If one of the nodes in your Q is marked "sticky at top of lists" and you hit the "top" or "bottom" action, the $position variable is reset for every entry in the 'nodequeue_nodes' table.

Attached is a snap of that table after I hit the 'top' action on a node that was 'sticky at the top of lists.' The instant before this action, the $position values were all aligned correctly (1, 2, 3 ...), as I had reset them by hand.

Oddly, the change did not affect qid 13, the NQ that I was editing at the time. So I think that the 'sticky' issue turned out to be a red herring.

The problem is here:

function nodequeue_queue_front($queue, $position) {
  if ($position < 2 || $position > $queue->count)
    return;
  $entry = db_fetch_object(db_query("SELECT * FROM {nodequeue_nodes} WHERE qid=$queue->qid AND position = $position"));
  db_query("DELETE FROM {nodequeue_nodes} WHERE qid=$queue->qid AND position = $position");
  db_query("UPDATE {nodequeue_nodes} SET position=position + 1 WHERE position < $position");
  db_query("INSERT INTO {nodequeue_nodes} (qid, nid, position) VALUES ($queue->qid, $entry->nid, 1)");
}

The UPDATE statement in the middle of that function needs a QID atached to it, doesn't it? Otherwise, it updates all nodes in the db.

I changed the following three lines:

function nodequeue_queue_front
   db_query("UPDATE {nodequeue_nodes} SET position=position + 1 WHERE position < $position AND qid=$queue->qid");
function nodequeue_queue_back
    db_query("UPDATE {nodequeue_nodes} SET position=position - 1 WHERE position > $position AND qid=$queue->qid");
function _nodequeue_queue_remove
      db_query("UPDATE {nodequeue_nodes} SET position = position - $diff WHERE position > $end AND qid=$queue->qid");

And I believe that fixes the issue.

Please test.

merlinofchaos’s picture

Status: Needs review » Fixed

I am blind. I looked at every one of those queries multiple times, but...well, I knew what I was supposed to be seeing and only saw that, I guess.

Fix put in and tested in both 4.6 and 4.7 versions. cron will run the packaging sometime today. Embarassment will last awhile.

agentrickard’s picture

Yeah, I was looking right at it too, at harping on the DELETE, when the real problem was the UPDATE.

Then I went on a looong hunt for differences in my assigned nodes to find the cause.....

Still, it was worth it b/c I love what this module does.

raema’s picture

Status: Fixed » Closed (fixed)
rajiv.singh’s picture

Version: » 7.x-2.0-beta1
Assigned: merlinofchaos » rajiv.singh
Priority: Normal » Major
Status: Closed (fixed) » Needs work
Issue tags: +nodequeue
StatusFileSize
new15.55 KB

Hi,
I am using 7.x-2.0-beta1 , the problem is still exists in this version. When I select 1 as position in two drop-downs(as in attached image) , one item gets deleted on submit.

rajiv.singh’s picture

Status: Needs work » Needs review
StatusFileSize
new621 bytes

I fixed the bug of #11 by applying this patch .
Please review.

rajiv.singh’s picture

StatusFileSize
new11.2 KB

Found One more issue :( The code at line:880
form_set_error($nid . '][position', t('Duplicate position value.'));

is not highlighting as error (border:red) to the select lists containing duplicate values.
It should be as attached image . going to submit another patch for it.

rajiv.singh’s picture

StatusFileSize
new1.01 KB

This patch is to fix issue in both #11 and #13

rajiv.singh’s picture

Status: Needs review » Fixed

this issue has been fixed by previous patch

agentrickard’s picture

Status: Fixed » Needs review

An issue can only be marked fixed if the patch has been committed.

fizk’s picture

Assigned: rajiv.singh » Unassigned
Issue summary: View changes
StatusFileSize
new1.46 KB

I just ran into this bug because the Javascript in nodequeue_dragdrop.js -> nodequeueUpdateNodePositions() was not consistently changing the selected options, which caused duplicate positions, and thus deleted queue entries. Here's the code I added to the patch in #14:

diff --git a/nodequeue_dragdrop.js b/nodequeue_dragdrop.js
index 406b76f..43e673a 100644
--- a/nodequeue_dragdrop.js
+++ b/nodequeue_dragdrop.js
@@ -162,6 +162,7 @@ function nodequeueUpdateNodePositions(table_id) {
 
   $('#' + table_id + ' tr').filter(":visible").find('.node-position').each(function(i) {
     $(this).val(size);
+    $(this).find("option[value='" + size + '"]').attr('selected', 'selected');
     reverse ? size-- : size++;
   });
 }

Status: Needs review » Needs work

The last submitted patch, 17: nodequeue-7.x-2.x-44017-17.patch, failed testing.

fizk’s picture

StatusFileSize
new1.7 KB

Actually, there's one more change. Too many elements were being included in the find():

diff --git a/nodequeue_dragdrop.js b/nodequeue_dragdrop.js
index 406b76f..cbf8f97 100644
--- a/nodequeue_dragdrop.js
+++ b/nodequeue_dragdrop.js
@@ -160,8 +160,9 @@ function nodequeueUpdateNodePositions(table_id) {
   var reverse = Drupal.settings.nodequeue.reverse[table_id.replace(/-/g, '_')];
   var size = reverse ? $('#' + table_id + ' .node-position').size() : 1;
 
-  $('#' + table_id + ' tr').filter(":visible").find('.node-position').each(function(i) {
+  $('#' + table_id + ' tr').filter(":visible").find('select.node-position').each(function(i) {
     $(this).val(size);
+    $(this).find("option[value='" + size + '"]').attr('selected', 'selected');
     reverse ? size-- : size++;
   });
 }

othermachines’s picture

Status: Needs work » Needs review
fizk’s picture

If someone can mark this RTBC after testing it, I can commit the patch.

othermachines’s picture

@fizk, do you have the steps to reproduce? It's all gotten a bit convoluted over 8 years so providing steps might be the ticket to getting this reviewed and fixed. I'll be your first tester. :)

fizk’s picture

@othermachines Thanks :) Try to create a queue with three items. Arrange them in a specific order, save that, then rearrange them in another order. Notice that it won't save according to the order you wanted.

othermachines’s picture

I can't seem to reproduce the delete behaviour when using drag-and-drop, only with row weights. I've revised the steps to reflect this.

Regarding the problem reported in #17 - I couldn't reproduce this at all. Maybe we need separate steps?

Steps to reproduce

1. Create queue with 3 items;

Result:
- First item
- Second item
- Third item

2. Using row weights (not drag-and-drop), change position of third item to [1];

3. Save.

Result:
- Third item
- Second item

First item is deleted.

othermachines’s picture

Version: 7.x-2.0-beta1 » 7.x-2.x-dev
Status: Needs review » Needs work

Patch in #19 needs an isset() check to avoid an "Undefined index" error when adding the first node to the queue.

function nodequeue_arrange_subqueue_form_validate($form, &$form_state) {
  $positions = array();
  
  // We only need to validate position on queues containing >= 1 node.
  if (isset($form_state['values']['nodes'])) {
    foreach ($form_state['values']['nodes'] as $nid => $element) {
      if (is_numeric($nid) && is_numeric($element['position'])) {
        $positions[$nid] = $element['position'];
      }
    }
    if (count(array_unique($positions)) < count($positions)) {
      $seen = array();
      foreach ($positions as $nid => $position) {
        if (isset($seen[$position])) {
          form_set_error('nodes][' . $nid . '][position', t('Duplicate position value.'));
        }
        $seen[$position] = TRUE;
      }
    }
  }
}

As mentioned above, I couldn't reproduce the problem in nodequeue_dragdrop.js, so couldn't test. Sorry. :-/

  • fizk committed a013948 on 7.x-2.x
    Issue #44017 by rajiv.singh, agentrickard, fizk, othermachines: Node...
fizk’s picture

Status: Needs work » Fixed

Committed with the isset(). Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.