I've run into a similar issue. We've got an installation with about 100 sites and we were seeing this behavior on multiple portals. I looked at the tab orders in the database and noticed that several tabs with the same ParentID (or that have a null Parent ID and are in the same portal) have the same value for the TabOrder. You can tell if your installation is having the same problem by running the following SQL query:
select count(*), TabOrder, level, ParentId, PortalID from Tabs
where IsDeleted = 0
group by level, TabOrder, PortalID, ParentId
having count(*) > 1
I isolated the problem on a local development install with two portals and was able to consistently reproduce the issue. I would make a change in the tab order for the first portal and the order of one or more tabs in the second portal would be affected. I even tried manually setting the tab order for all the tabs in both portals, but I could still move one page in one portal and have it affect the other.
After diving pretty deep into the DNN Platform source code I think I found the source of the problem. Dragging and dropping to reorder pages on the Admin -> Pages menu causes the Pages module to call the methods MoveTabAfter() and MoveTabBefore() in the TabController class, which in turn call the methods MoveTabAfter() and MoveTabBefore() on the DataProvider, which by default is the SQLDataProvider class.
The SQLDataProvider class simply calls the stored procedures dbo.MoveTabAfter and dbo.MoveTabBefore. Each of these takes as parameters the TabId of the tab being moved and the TabId of the "before" or "after" tab.
What I noticed is that if the two tabs in question have a non-null ParentID then things work fine. However, if the ParentID of both tabs is null (which happens for the top-level tabs in each portal) then it will update the TabOrder of every tab in the entire DNN installation that has a TabOrder higher than the tab in question. In other words the stored procedure to update Tab Order doesn't filter by Portal when the ParentID is null.
These stored procedures appear to be new as of version 6.2. It looks like before that there was a "UpdateTabOrder" method in the tab controller class. I tried to submit a bug through http://support.dnnsoftware.com, but even after registering for some reason I didn't have the right permissions to create an issue. I believe that the person who submitted the following issue may be experiencing the same problem:
https://dnntracker.atlassian.net/brow...
The solution appears to be to just modify the stored procedure to check that the tabs have the same PortalID when updating the tab order. I'm including the updated stored procedure below. Note that after fixing the problem you'll need to manually reset the TabOrder of any tabs having the same tab order as their siblings.
/********************************************************************************************************************/
/* MoveTabBefore stored
procedure
, updated
to
filter
by
PortalID
when
updating Tab
Order
. *****************************/
/* Provided
AS
-
IS
, without
any
warranty, even the implied warranty
of
usability. Use
at
your own risk. ****************/
/* I've tested this
on
my own installation
of
DNN
and
it appears
to
be functioning properly, but
as
always
execute
******/
/* extreme caution
and
backup everything before executing
on
your own install
of
DNN. **********************************/
/*********************************************************************************************************************/
USE [DNN]
GO
/****** Object: StoredProcedure [dbo].[MoveTabBefore] Script
Date
: 2/27/2014 3:31:58 PM ******/
SET
ANSI_NULLS
ON
GO
SET
QUOTED_IDENTIFIER
ON
GO
ALTER
PROCEDURE
[dbo].[MoveTabBefore]
@TabId
int
,
@BeforeTabId
int
,
@LastModifiedByUserID
int
AS
BEGIN
DECLARE
@OldParentId
int
DECLARE
@NewParentId
int
-- First we need to declare a PortalId variable
DECLARE
@PortalId
int
SET
@OldParentId = (
SELECT
ParentId
FROM
Tabs
WHERE
TabID = @TabId)
SET
@NewParentId = (
SELECT
ParentId
FROM
Tabs
WHERE
TabID = @BeforeTabId)
-- Now we initialize the PortalId based on the current portal
SET
@PortalId = (
Select
PortalId
FROM
Tabs
WHERE
TabID = @TabId)
DECLARE
@TabOrder
int
SET
@TabOrder = (
SELECT
TabOrder
FROM
Tabs
WHERE
TabID = @TabId)
IF (@OldParentId <> @NewParentId
OR
NOT
(@OldParentId
IS
NULL
AND
@NewParentId
IS
NULL
))
-- Parent has changed
BEGIN
-- update TabOrder of Tabs with same original Parent
UPDATE
Tabs
SET
TabOrder = TabOrder - 2
WHERE
(ParentId = @OldParentId
OR
(ParentId
IS
NULL
AND
@OldParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder > @TabOrder
-- Get TabOrder of AfterTab
SET
@TabOrder = (
SELECT
TabOrder
FROM
Tabs
WHERE
TabID = @BeforeTabId)
-- update TabOrder of Tabs with same new Parent
UPDATE
Tabs
SET
TabOrder = TabOrder + 2
WHERE
(ParentId = @NewParentId
OR
(ParentId
IS
NULL
AND
@NewParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder >= @TabOrder
-- Update Tab with new TabOrder
UPDATE
Tabs
SET
ParentId = @NewParentId,
TabOrder = @TabOrder,
LastModifiedByUserID = @LastModifiedByUserID,
LastModifiedOnDate = GETDATE()
WHERE
TabID = @TabId
EXEC
dbo.BuildTabLevelAndPath @TabId, 1
END
ELSE
-- Parent has not changed
BEGIN
-- Remove Tab from TabOrder
UPDATE
Tabs
SET
TabOrder = -1
WHERE
TabID = @TabId
-- Reorder
UPDATE
Tabs
SET
TabOrder = TabOrder - 2
WHERE
(ParentId = @OldParentId
OR
(ParentId
IS
NULL
AND
@OldParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder > @TabOrder
AND
TabId <> @TabId
-- Get TabOrder of BeforeTab
SET
@TabOrder = (
SELECT
TabOrder
FROM
Tabs
WHERE
TabID = @BeforeTabId)
-- Reorder
UPDATE
Tabs
SET
TabOrder = TabOrder + 2
WHERE
(ParentId = @OldParentId
OR
(ParentId
IS
NULL
AND
@OldParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder >= @TabOrder
-- Update Tab with new TabOrder
UPDATE
Tabs
SET
TabOrder = @TabOrder,
LastModifiedByUserID = @LastModifiedByUserID,
LastModifiedOnDate = GETDATE()
WHERE
TabID = @TabId
EXEC
dbo.BuildTabLevelAndPath @TabId
END
END
/*********************************************************************************************************************/
/* MoveTabAfter stored
procedure
, updated
to
filter
by
PortalID
when
updating Tab
Order
. *******************************/
/* Provided
AS
-
IS
, without
any
warranty, even the implied warranty
of
usability. Use
at
your own risk. *****************/
/* I've tested this
on
my own installation
of
DNN
and
it appears
to
be functioning properly, but
as
always
execute
*******/
/* extreme caution
and
backup everything before executing
on
your own install
of
DNN. **********************************/
/**********************************************************************************************************************/
USE [DNN]
GO
/****** Object: StoredProcedure [dbo].[MoveTabAfter] Script
Date
: 2/27/2014 3:27:53 PM ******/
SET
ANSI_NULLS
ON
GO
SET
QUOTED_IDENTIFIER
ON
GO
ALTER
PROCEDURE
[dbo].[MoveTabAfter]
@TabId
int
,
@AfterTabId
int
,
@LastModifiedByUserID
int
AS
BEGIN
DECLARE
@OldParentId
int
DECLARE
@NewParentId
int
-- First we need to declare a PortalId variable
DECLARE
@PortalId
int
SET
@OldParentId = (
SELECT
ParentId
FROM
Tabs
WHERE
TabID = @TabId)
SET
@NewParentId = (
SELECT
ParentId
FROM
Tabs
WHERE
TabID = @AfterTabId)
-- Now we initialize the PortalId based on the current portal
SET
@PortalId = (
Select
PortalId
FROM
Tabs
WHERE
TabID = @TabId)
DECLARE
@TabOrder
int
SET
@TabOrder = (
SELECT
TabOrder
FROM
Tabs
WHERE
TabID = @TabId)
IF (@OldParentId <> @NewParentId
OR
NOT
(@OldParentId
IS
NULL
AND
@NewParentId
IS
NULL
))
-- Parent has changed
BEGIN
-- update TabOrder of Tabs with same original Parent
UPDATE
Tabs
SET
TabOrder = TabOrder - 2
WHERE
(ParentId = @OldParentId
OR
(ParentId
IS
NULL
AND
@OldParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder > @TabOrder
-- Get TabOrder of AfterTab
SET
@TabOrder = (
SELECT
TabOrder
FROM
Tabs
WHERE
TabID = @AfterTabId)
-- update TabOrder of Tabs with same new Parent
UPDATE
Tabs
SET
TabOrder = TabOrder + 2
WHERE
(ParentId = @NewParentId
OR
(ParentId
IS
NULL
AND
@NewParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder > @TabOrder
-- Update Tab with new TabOrder
UPDATE
Tabs
SET
ParentId = @NewParentId,
TabOrder = @TabOrder + 2,
LastModifiedByUserID = @LastModifiedByUserID,
LastModifiedOnDate = GETDATE()
WHERE
TabID = @TabId
EXEC
dbo.BuildTabLevelAndPath @TabId, 1
END
ELSE
-- Parent has not changed
BEGIN
-- Remove Tab from TabOrder
UPDATE
Tabs
SET
TabOrder = -1
WHERE
TabID = @TabId
-- Reorder
UPDATE
Tabs
SET
TabOrder = TabOrder - 2
WHERE
(ParentId = @OldParentId
OR
(ParentId
IS
NULL
AND
@OldParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder > @TabOrder
AND
TabId <> @TabId
-- Get TabOrder of AfterTab
SET
@TabOrder = (
SELECT
TabOrder
FROM
Tabs
WHERE
TabID = @AfterTabId)
-- Reorder
UPDATE
Tabs
SET
TabOrder = TabOrder + 2
WHERE
(ParentId = @OldParentId
OR
(ParentId
IS
NULL
AND
@OldParentId
IS
NULL
))
-- Change each of our update statements to filter by the PortalId
AND
PortalID = @PortalId
AND
TabOrder > @TabOrder
-- Update Tab with new TabOrder
UPDATE
Tabs
SET
TabOrder = @TabOrder + 2,
LastModifiedByUserID = @LastModifiedByUserID,
LastModifiedOnDate = GETDATE()
WHERE
TabID = @TabId
EXEC
dbo.BuildTabLevelAndPath @TabId
END
END