/// <summary> /// This method is called just before a node is updated by the repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The node which will be modified.</param> /// <param name="modifiedProperties">The updated properties of the node.</param> protected override void DoBeforeUpdate(IMansionContext context, Node node, IPropertyBag modifiedProperties) { // if the name has not changed we are not interested string newName; if (!modifiedProperties.TryGet(context, "name", out newName)) return; // if the name has not changed after normalization we are not interested newName = TagUtilities.Normalize(newName); if (node.Pointer.Name.Equals(newName)) { modifiedProperties.Remove("name"); return; } modifiedProperties.Set("name", newName); // if the tag is renamed to another already existing tag, move all content to that existing tag and delete this one Node existingTag; var tagIndexNode = TagUtilities.RetrieveTagIndexNode(context); if (TagUtilities.TryRetrieveTagNode(context, tagIndexNode, newName, out existingTag)) { // TODO: move all content to the existing tag // TODO: delete this tag } }
/// <summary> /// Renders a navigation tree. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="navigationNode">Root <see cref="Node"/> of the navigation tree.</param> /// <param name="cssClasses">The CSS classes used for this navigation.</param> /// <returns>Returns the HTML of the navigation tree.</returns> public string Evaluate(IMansionContext context, Node navigationNode, string cssClasses) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (navigationNode == null) throw new ArgumentNullException("navigationNode"); if (string.IsNullOrEmpty(cssClasses)) throw new ArgumentNullException("cssClasses"); // get the current URL node var urlNode = context.Stack.Peek<Node>("UrlNode"); // retrieve all the children of the navigation root node var navigationItemNodeset = RetrieveNavigationItemNodeset(context, navigationNode); // build the tree structure var navigationTree = BuildTreeStructure(context, navigationNode, navigationItemNodeset, urlNode.Pointer); // render the tree using (context.Stack.Push("ControlProperties", new PropertyBag { {"cssClasses", cssClasses} })) return RenderNavigationTree(context, navigationTree); }
/// <summary> /// Builds the folder url of the specified <paramref name="folderNode"/>. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="assetTypeNode">The asset type node.</param> /// <param name="folderNode">The asset folder node.</param> /// <returns>Returns the <see cref="Url"/>.</returns> public static Url GetUrl(IMansionContext context, Node assetTypeNode, Node folderNode) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (assetTypeNode == null) throw new ArgumentNullException("assetTypeNode"); if (folderNode == null) throw new ArgumentNullException("folderNode"); // get the web context var webContext = context.Cast<IMansionWebContext>(); // get the path var folderPath = GetPath(assetTypeNode, folderNode); // create an url var url = Url.CreateUrl(webContext); // create the relative path url.PathSegments = WebUtilities.CombineIntoRelativeUrl(StaticContentRequestHandler.Prefix, folderPath); url.CanHaveExtension = true; // create the url return url; }
/// <summary> /// Generates a URL for <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionWebContext"/>.</param> /// <param name="node">The <see cref="Node"/> for which to generate the URL.</param> /// <returns>Returns the generated <see cref="Url"/>.</returns> public Url Generate(IMansionWebContext context, Node node) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (node == null) throw new ArgumentNullException("node"); // initialize the service Initialize(context); // get the node type var nodeType = typeService.Load(context, node.Pointer.Type); // get the generator NodeUrlGenerator generator; if (!generators.TryGetValue(nodeType, out generator)) throw new InvalidOperationException(string.Format("Could not find url generator for type {0}, does the type exist?", nodeType.Name)); // construct the base url var url = Url.CreateUrl(context); // generate the url generator.Generate(context, node, nodeType, url); // return the generated uri return url; }
/// <summary> /// Prepares an insert query. /// </summary> /// <param name="context"></param> /// <param name="connection">The connection.</param> /// <param name="transaction">The transaction.</param> /// <param name="sourceNode"></param> /// <param name="targetParentNode"></param> /// <returns></returns> public void Prepare(IMansionContext context, SqlConnection connection, SqlTransaction transaction, Node sourceNode, Node targetParentNode) { // validate arguments if (connection == null) throw new ArgumentNullException("connection"); if (transaction == null) throw new ArgumentNullException("transaction"); if (sourceNode == null) throw new ArgumentNullException("sourceNode"); if (targetParentNode == null) throw new ArgumentNullException("targetParentNode"); // get the properties of the new node var newProperties = new PropertyBag(sourceNode); // if the target pointer is the same as the parent of the source node, generate a new name to distinguish between the two. if (sourceNode.Pointer.Parent == targetParentNode.Pointer) { // get the name of the node var name = sourceNode.Get<string>(context, "name"); // change the name to indicate the copied node newProperties.Set("name", name + CopyPostfix); } // create an insert query command = context.Nucleus.CreateInstance<InsertNodeCommand>(); command.Prepare(context, connection, transaction, targetParentNode.Pointer, newProperties); }
/// <summary> /// </summary> /// <param name="context"></param> /// <param name="node"></param> /// <returns></returns> public bool Evaluate(IMansionContext context, Node node) { // valdidate arguments if (context == null) throw new ArgumentNullException("context"); return node != null; }
/// <summary> /// This method is called just before a node is updated by the repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The node which will be modified.</param> /// <param name="modifiedProperties">The updated properties of the node.</param> protected override void DoBeforeUpdate(IMansionContext context, Node node, IPropertyBag modifiedProperties) { // get the variables string currentTheme; var hasCurrentTheme = node.TryGet(context, "theme", out currentTheme) && !string.IsNullOrEmpty(currentTheme); string newTheme; var hasNewTheme = modifiedProperties.TryGet(context, "theme", out newTheme) && !string.IsNullOrEmpty(newTheme); // do nothing when the page does not have a theme yet if (!hasCurrentTheme) return; // retrieve the schema of the current theme var currentThemeSchema = ColumnSchema.GetSchema(context, currentTheme); // retrieve the blocks of this page var repository = context.Repository; var blockNodeset = repository.Retrieve(context, repository.ParseQuery(context, new PropertyBag { {"baseType", "Block"}, {"parentSource", node} })); // check if a new theme is selected if (hasNewTheme) { // retrieve the schema of the new theme var newThemeSchema = ColumnSchema.GetSchema(context, newTheme); // loop through the blocks to find obsolete ones foreach (var blockNode in blockNodeset.Nodes) { // get the column of this block var column = blockNode.Get<string>(context, "column"); // check if this block lived in the old theme if (!currentThemeSchema.ContainsColumn(column)) continue; // check if the column exists in the new theme as well if (newThemeSchema.ContainsColumn(column)) continue; // block is obsolete delete it repository.Delete(context, blockNode.Pointer); } } else { // theme is removed, delete all the theme blocks foreach (var blockNode in blockNodeset.Nodes.Where(candidate => currentThemeSchema.ContainsColumn(candidate.Get<string>(context, "column")))) repository.Delete(context, blockNode.Pointer); } base.DoBeforeUpdate(context, node, modifiedProperties); }
/// <summary> /// Generates a URL for the node. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The <see cref="Node"/> for which to generate the URL.</param> /// <returns>The <see cref="Uri"/> generated for the <paramref name="node"/>.</returns> public Url Evaluate(IMansionContext context, Node node) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (node == null) throw new ArgumentNullException("node"); // return the url return nodeUrlService.Generate(context.Cast<IMansionWebContext>(), node); }
/// <summary> /// Builds the asset path of the specified <paramref name="folderNode"/>. /// </summary> /// <param name="assetTypeNode">The asset type node.</param> /// <param name="folderNode">The asset folder node.</param> /// <returns>Returns the path.</returns> public static string GetPath(Node assetTypeNode, Node folderNode) { // validate arguments if (assetTypeNode == null) throw new ArgumentNullException("assetTypeNode"); if (folderNode == null) throw new ArgumentNullException("folderNode"); // build the url return string.Join("/", new[] {"Assets"}.Concat(folderNode.Pointer.Path.Skip(assetTypeNode.Pointer.Depth - 1))); }
/// <summary> /// Updates an existing node in this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The node which will be updated.</param> /// <param name="properties">The properties which to update.</param> protected override Node DoUpdateNode(IMansionContext context, Node node, IPropertyBag properties) { // update the node repository.UpdateNode(context, node, properties); // merge the properties node.Merge(properties); // return the updated node return node; }
/// <summary> /// Checks whether the candidate child is a child of the parent. /// </summary> /// <param name="context"></param> /// <param name="child"></param> /// <param name="parent"></param> /// <returns></returns> public bool Evaluate(IMansionContext context, Node child, Node parent) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (child == null) throw new ArgumentNullException("child"); if (parent == null) throw new ArgumentNullException("parent"); return child.Pointer.IsChildOf(parent.Pointer); }
/// <summary> /// This method is called just before a node is created by the repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="parent">The parent node to which the new child will be added.</param> /// <param name="newProperties">The new properties of the node.</param> protected override void DoBeforeCreate(IMansionContext context, Node parent, IPropertyBag newProperties) { // check if the layout is set string layout; if (newProperties.TryGet(context, "layout", out layout)) return; if (!parent.TryGet(context, "layout", out layout)) layout = "OneColumnLayout"; // set the layout newProperties.TrySet("layout", layout); }
/// <summary> /// This method is called when an property which is not on the node is accessed. Useful for lazy loading properties. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The <see cref="Node"/> which does not have the property..</param> /// <param name="propertyName">The name of the property being retrieved.</param> /// <param name="value">The missing value</param> protected override bool DoTryResolveMissingProperty(IMansionContext context, Node node, string propertyName, out object value) { // we do not care about any property except _tags if (!"_tags".Equals(propertyName, StringComparison.OrdinalIgnoreCase)) { value = null; return false; } // initialize the _tags attribute value = TagUtilities.ToNames(context, node); return true; }
/// <summary> /// Updates an existing node in this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The node which will be updated.</param> /// <param name="modifiedProperties">The properties which to update.</param> public void UpdateNode(IMansionContext context, Node node, IPropertyBag modifiedProperties) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (node == null) throw new ArgumentNullException("node"); if (modifiedProperties == null) throw new ArgumentNullException("modifiedProperties"); CheckDisposed(); // invoke template method DoUpdateNode(context, node, modifiedProperties); }
/// <summary> /// Generates an URL for the <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionWebContext"/>.</param> /// <param name="node">The <see cref="Node"/> for which to generate the URL.</param> /// <param name="nodeType">The <see cref="ITypeDefinition"/> of the node.</param> /// <param name="url">The <see cref="Url"/> which to use to build the url.</param> public void Generate(IMansionWebContext context, Node node, ITypeDefinition nodeType, Url url) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (node == null) throw new ArgumentNullException("node"); if (nodeType == null) throw new ArgumentNullException("nodeType"); if (url == null) throw new ArgumentNullException("url"); // invoke template method DoGenerate(context, node, nodeType, url); }
/// <summary> /// Creates an instance of <see cref="AssetType"/> from <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The <see cref="Node"/>.</param> /// <returns>Returns the created <see cref="AssetType"/>.</returns> public static AssetType Create(IMansionContext context, Node node) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (node == null) throw new ArgumentNullException("node"); return new AssetType { Label = node.Pointer.Name, BasePath = "/" + node.Pointer.Name.ToLower() + "/", Node = node, Url = AssetUtil.GetUrl(context, node, node) }; }
/// <summary> /// Creates an <see cref="AssetEntry"/> instance from <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="folder">The <see cref="AssetFolder"/>.</param> /// <param name="node">The <see cref="Node"/>.</param> /// <returns>Returns the created <see cref="AssetEntry"/> instance.</returns> public static AssetEntry Create(IMansionContext context, AssetFolder folder, Node node) { //validate arguments if (context == null) throw new ArgumentNullException("context"); if (folder == null) throw new ArgumentNullException("folder"); if (node == null) throw new ArgumentNullException("node"); return new AssetEntry { AssetFolder = folder, Name = node.Pointer.Name, Size = node.Get<long>(context, "size", 0), ModificationDate = node.Get(context, "modified", DateTime.MinValue) }; }
/// <summary> /// Generates an URL for the <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionWebContext"/>.</param> /// <param name="node">The <see cref="Node"/> for which to generate the URL.</param> /// <param name="nodeType">The <see cref="ITypeDefinition"/> of the node.</param> /// <param name="url">The <see cref="UriBuilder"/> which to use to build the url.</param> protected override void DoGenerate(IMansionWebContext context, Node node, ITypeDefinition nodeType, Url url) { // get the site node from the stack Node siteNode; if (!context.Stack.TryPeek("SiteNode", out siteNode)) throw new InvalidOperationException("Could not find sitenode on the stack, please check application configuration."); // resolve the template page for this node Node templatePageNode; if (!portalService.TryResolveTemplatePage(context, siteNode, node, out templatePageNode)) throw new InvalidOperationException(string.Format("Could not find template page for node {0} ({1})", node.Pointer.PathString, node.Pointer.PointerString)); // construct the path var sitePathParts = templatePageNode.Pointer.Hierarchy.SkipWhile(candidate => !siteNode.Pointer.IsParentOf(candidate)).TakeWhile(candidate => candidate.Id != templatePageNode.Pointer.Id).ToArray(); var contentPathParts = node.Pointer.Hierarchy.Skip(2).ToArray(); url.PathSegments = new[] {node.Pointer.Id.ToString(CultureInfo.InvariantCulture)}.Concat(sitePathParts.Select(pointer => pointer.Name)).Concat(contentPathParts.Select(pointer => pointer.Name)).ToArray(); }
/// <summary> /// Creates a new node in this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="parent">The parent node.</param> /// <param name="newProperties">The properties of the node which to create.</param> /// <returns>Returns the created nodes.</returns> public Node CreateNode(IMansionContext context, Node parent, IPropertyBag newProperties) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (parent == null) throw new ArgumentNullException("parent"); if (newProperties == null) throw new ArgumentNullException("newProperties"); CheckDisposed(); // if no allowedRoleGuids are specified, copy the parens if (!newProperties.Contains("allowedRoleGuids")) newProperties.Set("allowedRoleGuids", parent.Get(context, "allowedRoleGuids", string.Empty)); // invoke template method return DoCreateNode(context, parent, newProperties); }
/// <summary> /// This method is called just before a node is updated by the repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The node which will be modified.</param> /// <param name="modifiedProperties">The updated properties of the node.</param> protected override void DoBeforeUpdate(IMansionContext context, Node node, IPropertyBag modifiedProperties) { // check if the layout was not modified string newLayoutName; if (!modifiedProperties.TryGet(context, "layout", out newLayoutName)) return; // check if there was no old layout string oldLayoutName; if (!node.TryGet(context, "layout", out oldLayoutName)) return; // get the schemas var newColumnSchema = ColumnSchema.GetSchema(context, newLayoutName); var oldColumnSchema = ColumnSchema.GetSchema(context, oldLayoutName); // retrieve the blocks of this page var repository = context.Repository; var blockNodeset = repository.Retrieve(context, repository.ParseQuery(context, new PropertyBag { {"baseType", "Block"}, {"parentSource", node} })); // loop through all the nodes foreach (var blockNode in blockNodeset.Nodes) { // check if this block was not in a column of the old schema so it wont have to move to the new schema var columnName = blockNode.Get<string>(context, "column"); if (!oldColumnSchema.ContainsColumn(columnName)) continue; // check if the column is in the new schema as well so it wont have to move if (newColumnSchema.ContainsColumn(columnName)) continue; // move the block to the default column repository.Update(context, blockNode, new PropertyBag { {"column", newColumnSchema.DefaultColumn} }); } }
/// <summary> /// Generates an URL for the <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionWebContext"/>.</param> /// <param name="node">The <see cref="Node"/> for which to generate the URL.</param> /// <param name="nodeType">The <see cref="ITypeDefinition"/> of the node.</param> /// <param name="url">The <see cref="Url"/> which to use to build the url.</param> protected override void DoGenerate(IMansionWebContext context, Node node, ITypeDefinition nodeType, Url url) { // check if the site node has a prefered host header, or pick the first hostheader string preferedHostheader; String hostheaderString; if (node.TryGet(context, "preferedHostheader", out preferedHostheader) && !string.IsNullOrEmpty(preferedHostheader)) url.HostName = preferedHostheader; else if (node.TryGet(context, "hostheaders", out hostheaderString)) { var hostheaders = hostheaderString.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList(); // if there are not hostheaders set or the current hostheader is in the hostheaders dont change the host if (hostheaders.Count == 0 || hostheaders.Contains(url.HostName, StringComparer.OrdinalIgnoreCase)) return; // set the new hostheader url.HostName = hostheaders[0]; } }
/// <summary> /// Creates an <see cref="AssetFolder"/> instance from the <paramref name="node"/>. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="assetType">The <see cref="AssetType"/>.</param> /// <param name="node">The <see cref="Node"/>.</param> /// <returns>Returns the constructed <see cref="AssetFolder"/> instance.</returns> public static AssetFolder Create(IMansionContext context, AssetType assetType, Node node) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (assetType == null) throw new ArgumentNullException("assetType"); if (node == null) throw new ArgumentNullException("node"); // create the folder return new AssetFolder { AssetType = assetType, Path = "/" + node.Pointer.Name.ToLower() + "/", Label = node.Pointer.Name, Node = node, Url = AssetUtil.GetUrl(context, assetType.Node, node) }; }
/// <summary> /// This method is called just after a node is created by the repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="parent">The parent node to which the new child was be added.</param> /// <param name="node">The created node.</param> /// <param name="newProperties">The properties from which the <paramref name="node"/> was constructed.</param> protected override void DoAfterCreate(IMansionContext context, Node parent, Node node, IPropertyBag newProperties) { // get the layout schema string layoutName; if (!node.TryGet(context, "layout", out layoutName)) throw new InvalidOperationException(string.Format("Template page {0} ({1}) does not have a layout", node.Pointer.PathString, node.Pointer.PointerString)); var layoutSchema = ColumnSchema.GetSchema(context, layoutName); // add the content detail block to the primary column context.Repository.Create(context, node, new PropertyBag { {"name", "Content Detail Block"}, {"type", "ContentDetailBlock"}, {"approved", true}, {"column", layoutSchema.DefaultColumn} }); // invoke base base.DoAfterCreate(context, parent, node, newProperties); }
/// <summary> /// This method is called just after a node is updated by the repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The modified node.</param> /// <param name="modifiedProperties">The properties which were set to the updated <paramref name="node"/>.</param> protected override void DoAfterUpdate(IMansionContext context, Node node, IPropertyBag modifiedProperties) { // loop over all the updated properties to find those who start with _propagate foreach (var propagatePropertyName in modifiedProperties.Names.Where(x => x.StartsWith(PropagatePrefix, StringComparison.OrdinalIgnoreCase))) { // get the name of the property being propagated var propagatedPropertyName = propagatePropertyName.Substring(PropagatePrefix.Length); // get the propagated property value var propagatedPropertyValue = modifiedProperties.Get<object>(context, propagatedPropertyName); // retrieve all the children nodes and update them all foreach (var childNode in context.Repository.Retrieve(context, new PropertyBag { {"parentSource", node}, {"depth", "any"} }).Nodes) { context.Repository.Update(context, childNode, new PropertyBag { {propagatedPropertyName, propagatedPropertyValue} }); } } }
/// <summary> /// Deletes an existing node from this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The pointer to the node which will be deleted.</param> public void DeleteNode(IMansionContext context, Node node) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (node == null) throw new ArgumentNullException("node"); CheckDisposed(); // invoke template method DoDeleteNode(context, node); }
/// <summary> /// Retrieves the tag <see cref="Node"/> with name <paramref name="tagName"/>. Creates one when no exists. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="tagIndexNode">The tag index node.</param> /// <param name="tagName">The name of the tag.</param> /// <returns>Returns the <see cref="Node"/> representing the tag index.</returns> public static Node RetrieveTagNode(IMansionContext context, Node tagIndexNode, string tagName) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (tagIndexNode == null) throw new ArgumentNullException("tagIndexNode"); tagName = Normalize(tagName); if (string.IsNullOrEmpty(tagName)) throw new ArgumentNullException("tagName"); // get the repository var repository = context.Repository; // retrieve the tag or create it return repository.RetrieveSingleNode(context, new PropertyBag { {"parentSource", tagIndexNode}, {"name", tagName}, {"type", "Tag"} }) ?? repository.CreateNode(context, tagIndexNode, new PropertyBag { {"name", tagName}, {"type", "Tag"}, {"approved", true}, }); }
/// <summary> /// Tries to retrieve the tag <see cref="Node"/> with name <paramref name="tagName"/>. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="tagIndexNode">The tag index node.</param> /// <param name="tagName">The name of the tag.</param> /// <param name="tagNode"></param> /// <returns>Returns true when the node was found, otherwise false.</returns> public static bool TryRetrieveTagNode(IMansionContext context, Node tagIndexNode, string tagName, out Node tagNode) { // validate arguments if (context == null) throw new ArgumentNullException("context"); if (tagIndexNode == null) throw new ArgumentNullException("tagIndexNode"); tagName = Normalize(tagName); if (string.IsNullOrEmpty(tagName)) throw new ArgumentNullException("tagName"); // get the repository var repository = context.Repository; // retrieve the node tagNode = repository.RetrieveSingleNode(context, new PropertyBag { {"parentSource", tagIndexNode}, {"name", tagName}, {"type", "Tag"} }); return tagNode != null; }
/// <summary> /// Deletes an existing node from this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The pointer to the node which will be deleted.</param> protected abstract void DoDeleteNode(IMansionContext context, Node node);
/// <summary> /// Updates an existing node in this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="node">The node which will be updated.</param> /// <param name="modifiedProperties">The properties which to update.</param> protected abstract void DoUpdateNode(IMansionContext context, Node node, IPropertyBag modifiedProperties);
/// <summary> /// Creates a new node in this repository. /// </summary> /// <param name="context">The <see cref="IMansionContext"/>.</param> /// <param name="parent">The parent node.</param> /// <param name="newProperties">The properties of the node which to create.</param> /// <returns>Returns the created nodes.</returns> protected abstract Node DoCreateNode(IMansionContext context, Node parent, IPropertyBag newProperties);