public void EnsureRootNoteExists()
        {
            Document root = this.documentRepository.GetById( Document.ROOT_NODE_ID );
            if ( root != null ) {
                return; // It does
            }

            // It doesn't, create it

            /* SQL:
            TRUNCATE TABLE dbo.Document
            SET IDENTITY_INSERT dbo.Document ON
            INSERT INTO dbo.Document (
                DocumentId, NodeTypeId, ParentDocumentId, NodeIdPath, Depth, Slug,
                Name, PageContent, HeadContent, ScriptContent,
                CreatedDate, ModifiedDate, IsActive
            ) VALUES (
                1, 1, 1, '/1', 0, '',
                'CMS', '', NULL, NULL,
                GETDATE(), GETDATE(), 1
            )
            SET IDENTITY_INSERT dbo.Document ON
            */

            // FRAGILE: The code below doesn't work at all, but the SQL above works just fine

            root = new Document {
                Depth = 0,
                NodeType = NodeType.Root,
                NodeIdPath = "/1",
                Name = "CMS",
                Slug = "",
                PageContent = ""
            };
            this.documentRepository.Save( root ); // FRAGILE: This fails because root's slug is blank
            root.ParentDocumentId = root.DocumentId;
            this.documentRepository.Save( root );

            if ( root.DocumentId != Document.ROOT_NODE_ID ) {
                throw new ArgumentOutOfRangeException( "Error creating root node, created id " + root.DocumentId + " instead, truncate and reseed the table" );
            }
        }
        // TODO: Move this to a trigger or sproc to get transaction?
        public void SaveModifiedNode( Document ToSave )
        {
            /* FRAGILE: ASSUME: Properties validated previously:
             * - Slug isn't null
             * - Slug is unique among siblings
             * - ParentId is valid
            */

            // Stash original data so we can compare later
            int origParentId = 0;
            string origNodeIdPath = null;
            int origDepth = 0;
            if ( !ToSave.IsNew() ) {
                Document orig = this.documentRepository.GetNode( ToSave.Id, ActiveOnly: false );
                if ( orig == null ) {
                    // TODO: throw up();
                } else {
                    origParentId = orig.ParentDocumentId;
                    origNodeIdPath = orig.NodeIdPath;
                    origDepth = orig.Depth;
                }
            } else {
                // Get a NodeId
                ToSave.NodeIdPath = "NOT NULL"; // Defeat [Required] temporarily -- safer than filling it with an incorrect NodeId
                this.documentRepository.Save( ToSave );
            }

            // New or parent id changed? Set depth and path
            if ( ToSave.IsNew() || origParentId != ToSave.ParentDocumentId ) {
                this.ResetPathAndDepth( ToSave );
            }

            // Save
            this.documentRepository.Save( ToSave );

            // Update children's depth / path (recursively)
            this.UpdateChildrenPathAndDepth( origNodeIdPath, origDepth, ToSave.NodeIdPath, ToSave.Depth );
        }
 private DocumentBranchEditViewModel NodeToBranchModel( Document Node )
 {
     return new DocumentBranchEditViewModel {
         DocumentId = Node.DocumentId,
         ParentDocumentId = Node.ParentDocumentId,
         Name = Node.Name,
         Slug = Node.Slug,
         IsActive = Node.IsActive,
     };
 }
 public void ResetPathAndDepth( Document Node )
 {
     Document parent = this.documentRepository.GetNode( Node.ParentDocumentId, ActiveOnly: false );
     if ( parent == null ) {
         throw new ArgumentNullException( "Node", "NodeId " + Node.ParentDocumentId + " not found" );
     }
     Node.Depth = parent.Depth + 1; // Set depth
     Node.NodeIdPath = parent.NodeIdPath + Document.NODE_PATH_SEPARATOR + Node.Id; // Set path
 }
 private void BranchModelToNode( DocumentBranchEditViewModel Model, Document Node )
 {
     Node.Name = Model.Name;
     Node.Slug = this.documentService.SanitizeSlug( Model.Slug, Model.Name, Document.SLUG_MAX_LENGTH );
     Node.IsActive = Model.IsActive;
 }
        private ActionResult SaveBranchNode( DocumentBranchEditViewModel Model )
        {
            if ( Model == null ) {
                // Fix your errors and try again
                return this.View( "NotFound" );
            }
            if ( Model.DocumentId == Document.ROOT_NODE_ID ) {
                this.ModelState.AddModelError( "DocumentId", "Can't set root node" );
            }
            if ( !this.documentRepository.NodeExists( Model.ParentDocumentId, ActiveOnly: false ) ) {
                this.ModelState.AddModelError( "ParentDocumentId", "Node has an invalid parent" );
            }
            if ( !this.ModelState.IsValid ) {
                // Fix your errors and try again
                this.SetupBreadcrumb( Model );
                return this.View( "Edit", Model );
            }

            Document node = null;
            if ( Model.DocumentId > 0 ) {
                node = this.documentRepository.GetNode( Model.DocumentId, false );
                if ( node == null ) {
                    return this.View( "NotFound" );
                }
            } else {
                node = new Document {
                    NodeType = NodeType.Branch,
                    ParentDocumentId = Model.ParentDocumentId
                };
            }

            this.BranchModelToNode( Model, node );
            bool uniqueSlug = this.documentRepository.SlugUniqueAmongSiblings( node.ParentDocumentId, node.Slug, node.DocumentId );
            if ( !uniqueSlug ) {
                this.ModelState.AddModelError( "Slug", "Slug is not unique for nodes in this category" );
                return this.View( "Edit", Model );
            }

            this.documentService.SaveModifiedNode( node );

            // Saved successfully
            return this.RedirectToAction( "Index", "Document" );
        }
 private DocumentLeafEditViewModel NodeToLeafModel( Document Document )
 {
     DocumentLeafEditViewModel model = new DocumentLeafEditViewModel {
         DocumentId = Document.DocumentId,
         ParentDocumentId = Document.ParentDocumentId,
         Name = Document.Name,
         Slug = Document.Slug,
         IsActive = Document.IsActive,
         PageContent = Document.PageContent,
         HeadContent = Document.HeadContent,
         ScriptContent = Document.ScriptContent
     };
     return model;
 }
 private void LeafModelToNode( DocumentLeafEditViewModel Model, Document Document )
 {
     Document.Name = Model.Name;
     Document.Slug = Model.Slug;
     Document.IsActive = Model.IsActive;
     Document.PageContent = Model.PageContent;
     Document.HeadContent = Model.HeadContent;
     Document.ScriptContent = Model.ScriptContent;
 }
        private ActionResult SaveLeafNode( DocumentLeafEditViewModel Model, int DestinationDocumentId )
        {
            if ( Model == null ) {
                // Fix your errors and try again
                if ( this.Request.IsAjaxRequest() ) {
                    return this.HttpNotFound();
                } else {
                    return this.View( "NotFound" );
                }
            }
            if ( Model.DocumentId == Document.ROOT_NODE_ID ) {
                this.ModelState.AddModelError( "DocumentId", "Can't set root node" );
            }
            if ( !this.documentRepository.NodeExists( Model.ParentDocumentId, ActiveOnly: false ) ) {
                this.ModelState.AddModelError( "ParentDocumentId", "Document has an invalid parent" );
            }
            if ( !this.ModelState.IsValid ) {
                // Fix your errors and try again
                if ( this.Request.IsAjaxRequest() ) {
                    return this.Json( new { Success = false, Reason = "Data validation failure", Validation = this.ModelState, Model } );
                } else {
                    this.SetupBreadcrumb( Model );
                    return this.View( "Edit", Model );
                }
            }

            Document node = null;
            if ( Model.DocumentId > 0 ) {
                node = this.documentRepository.GetById( Model.DocumentId );
                if ( node == null ) {
                    if ( this.Request.IsAjaxRequest() ) {
                        return this.HttpNotFound();
                    } else {
                        return this.View( "NotFound" );
                    }
                }
            } else {
                // Add new node
                node = new Document {
                    NodeType = NodeType.Leaf,
                    ParentDocumentId = Model.ParentDocumentId
                };
            }

            // Copy Model to Enties
            this.LeafModelToNode( Model, node );

            // Did they leave anything blank?
            node.Slug = this.documentService.SanitizeSlug( node.Slug, node.Name, Document.SLUG_MAX_LENGTH );

            bool uniqueSlug = this.documentRepository.SlugUniqueAmongSiblings( node.ParentDocumentId, node.Slug, node.DocumentId );
            if ( !uniqueSlug ) {
                this.ModelState.AddModelError( "Slug", "Slug is not unique for nodes in this category" );
                if ( this.Request.IsAjaxRequest() ) {
                    return this.Json( new { Success = false, Reason = "Data validation failure", Validation = this.ModelState, Model } );
                } else {
                    return this.View( "Edit", Model );
                }
            }

            this.documentService.SaveModifiedNode( node );
            // Saved successfully

            string pathInfo = this.documentService.GetSlugPath( DestinationDocumentId );
            string url = this.urlService.JoinUrl( MvcApplication.DOCUMENT_BASE_PATH, pathInfo );

            Model = this.NodeToLeafModel( node ); // Reset model
            if ( this.Request.IsAjaxRequest() ) {
                return this.Json( new { Success = true, Redirect = url, Model } );
            } else if ( !string.IsNullOrEmpty( url ) ) {
                return this.Redirect( url );
            } else {
                return this.View( "Edit", Model );
            }
        }