private static User GetUserFromString(string user) { User targetUser = null; if (!String.IsNullOrEmpty(user)) { int userId; if (Int32.TryParse(user, out userId)) { targetUser = Node.LoadNode(userId) as User; } else if (RepositoryPath.IsValidPath(user) == RepositoryPath.PathResult.Correct) { targetUser = Node.LoadNode(user) as User; } else { throw new ArgumentException("The 'user' parameter cannot be recognized as a path or an Id: " + user); } if (targetUser == null) { throw new ArgumentException("User not found by the parameter: " + user); } } return(targetUser); }
public override void Execute(ExecutionContext context) { context.AssertRepositoryStarted(); var sourcePath = ResolvePackagePath(Source, context); if (!IO.Directory.Exists(sourcePath) && !IO.File.Exists(sourcePath)) { throw new PackagingException(SR.Errors.Import.SourceNotFound); } var checkResult = RepositoryPath.IsValidPath(Target); if (checkResult != RepositoryPath.PathResult.Correct) { if (!Target.StartsWith("/root", StringComparison.OrdinalIgnoreCase)) { throw new PackagingException(SR.Errors.Import.InvalidTarget, RepositoryPath.GetInvalidPathException(checkResult, Target)); } } if (!Node.Exists(Target)) { throw new PackagingException(SR.Errors.Import.TargetNotFound); } base.DoImport(null, sourcePath, Target); }
public ExportContext(string sourceRepositoryPath, string targetFsPath) { if (RepositoryPath.IsValidPath(sourceRepositoryPath) != RepositoryPath.PathResult.Correct) { throw new InvalidPathException("Source path is invalid: " + sourceRepositoryPath); } SourceFsPath = sourceRepositoryPath; CurrentDirectory = targetFsPath; _sourceRoot = String.Concat(sourceRepositoryPath, sourceRepositoryPath.EndsWith("/") ? "" : "/"); _outerReferences = new List <string>(); if (sourceRepositoryPath == Repository.RootPath) { ContentTypeDirectory = Path.Combine(targetFsPath, Repository.RootName); ContentTypeDirectory = Path.Combine(ContentTypeDirectory, Repository.SystemFolderName); ContentTypeDirectory = Path.Combine(ContentTypeDirectory, Repository.SchemaFolderName); ContentTypeDirectory = Path.Combine(ContentTypeDirectory, Repository.ContentTypesFolderName); } else if (sourceRepositoryPath == Repository.SystemFolderPath) { ContentTypeDirectory = Path.Combine(targetFsPath, Repository.SystemFolderName); ContentTypeDirectory = Path.Combine(ContentTypeDirectory, Repository.SchemaFolderName); ContentTypeDirectory = Path.Combine(ContentTypeDirectory, Repository.ContentTypesFolderName); } else if (sourceRepositoryPath == Repository.SchemaFolderPath) { ContentTypeDirectory = Path.Combine(targetFsPath, Repository.SchemaFolderName); ContentTypeDirectory = Path.Combine(ContentTypeDirectory, Repository.ContentTypesFolderName); } else if (sourceRepositoryPath == Repository.ContentTypesFolderPath) { ContentTypeDirectory = Path.Combine(targetFsPath, Repository.ContentTypesFolderName); } }
/// <summary>Method that called by the packaging framework.</summary> public override void Execute(ExecutionContext context) { // Deleting a repository content if the given path is repository path if (RepositoryPath.IsValidPath(Path) == RepositoryPath.PathResult.Correct) { // Deleting a repository content if it exists if (Node.Exists(Path)) { Logger.LogMessage("Deleting content: " + Path); Node.ForceDelete(Path); } // Displaying a simple message. else { Logger.LogMessage("Content was not found: " + Path); } return; } // Getting local and network paths in one array. var paths = ResolveAllTargets(Path, context); // Executing deletion on all servers. foreach (var path in paths) { Execute(path); } }
public static string TakeLockOver(Content content, string user) { IUser targetUser = null; if (!String.IsNullOrEmpty(user)) { int userId; if (Int32.TryParse(user, out userId)) { targetUser = Node.LoadNode(userId) as IUser; } else if (RepositoryPath.IsValidPath(user) == RepositoryPath.PathResult.Correct) { targetUser = Node.LoadNode(user) as IUser; } else { throw new ArgumentException("The 'user' parameter cannot be recognized as a path or an Id: " + user); } if (targetUser == null) { throw new ArgumentException("User not found by the parameter: " + user); } } content.ContentHandler.Lock.TakeLockOver(targetUser); return("Ok"); }
protected virtual TNode FindNearestItem(string path, Func <string, string> transform) { var pr = RepositoryPath.IsValidPath(path); if (pr != RepositoryPath.PathResult.Correct) { throw RepositoryPath.GetInvalidPathException(pr, path); } var p = path.ToLowerInvariant(); TNode tnode; while (true) { if (Items.TryGetValue(transform(p), out tnode)) { return(tnode); } if (p == "/root" || p == "/" || p == string.Empty) { break; } p = RepositoryPath.GetParentPath(p); } return(null); }
protected virtual TNode[] FindNearestItems(string path, Func <string, string> transform) { var pr = RepositoryPath.IsValidPath(path); if (pr != RepositoryPath.PathResult.Correct) { throw RepositoryPath.GetInvalidPathException(pr, path); } var p = path.ToLowerInvariant(); while (true) { // find all items in the same folder var items = Items.Where(kv => string.Compare(RepositoryPath.GetParentPath(kv.Key), transform(p), StringComparison.InvariantCultureIgnoreCase) == 0) .Select(kv => kv.Value).ToArray(); if (items.Length > 0) { return(items); } if (p == "/root" || p == "/" || p == string.Empty) { break; } p = RepositoryPath.GetParentPath(p); } return(null); }
protected override bool ParseValue(string value) { var refList = new List <Node>(); foreach (var p in value.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)) { var path = p.Trim(); if (RepositoryPath.IsValidPath(path) != RepositoryPath.PathResult.Correct) { continue; } var node = Node.LoadNode(path); if (node == null) { continue; } refList.Add(node); } var slotType = GetHandlerSlot(0); if (slotType == typeof(IEnumerable)) { this.SetData(refList); return(true); } else if (slotType == typeof(Node)) { this.SetData(refList.Count == 0 ? null : refList[0]); return(true); } throw new NotSupportedException(String.Concat("ReferenceField not supports this conversion: Node or IEnumerable to ", slotType.FullName)); }
public override void Execute(ExecutionContext context) { var source = (context.ResolveVariable(Source) as string) ?? "/Root"; var target = context.ResolveVariable(Target) as string; var filter = context.ResolveVariable(Filter) as string; context.AssertRepositoryStarted(); try { string contextPath = null, queryString = null; if (string.IsNullOrWhiteSpace(source) && string.IsNullOrWhiteSpace(filter)) { throw new InvalidStepParameterException("Missing Source or Filter argument."); } if (!string.IsNullOrWhiteSpace(source)) { contextPath = ResolveRepositoryPath(source, context); if (RepositoryPath.IsValidPath(contextPath) != RepositoryPath.PathResult.Correct) { throw new InvalidStepParameterException("Invalid repository path."); } } else { contextPath = "/Root"; } if (!string.IsNullOrWhiteSpace(filter)) { queryString = (string)context.ResolveVariable(filter); } var targetFolder = ResolveTargetPath(target, context, TargetPathRelativeTo.AppData); var savedMode = RepositoryEnvironment.WorkingMode.Exporting; RepositoryEnvironment.WorkingMode.SetExporting(true); try { Exporter.Export(contextPath, targetFolder, queryString); } catch (Exception e) { Logger.LogMessage(@"Export ends with error:\n"); Logger.LogMessage(e + @"\n"); Logger.LogMessage(e.StackTrace + @"\n"); } finally { RepositoryEnvironment.WorkingMode.SetExporting(savedMode); } } catch (InvalidStepParameterException) { Logger.LogMessage("Export step can work with valid paths only."); throw; } }
public void Path_InvalidPathChar() { // try some characters that are invalid (path invalidcharpattern is [^a-zA-Z0-9.()[\\]\\-_ /]) var chars = new char[] { '\\', ':', '?', '"', '<', '>', '|', ',', ';', '&', '#', '+', '[', ']', '*' }; foreach (char c in chars) { Assert.IsTrue(RepositoryPath.IsValidPath(String.Concat("/Root/a", c, "b")) == RepositoryPath.PathResult.InvalidPathChar, string.Concat("Not recognised invalid path char: ", c)); } }
public void Path_ValidPathChar() { // try some characters that are valid (see the invalidcharpattern in the RepositoryPath class) var chars = new char[] { 'b', 'V', '4', '.', '(', ')', '-', '_', ' ', '/' }; foreach (char c in chars) { Assert.IsTrue(RepositoryPath.IsValidPath(String.Concat("/Root/a", c, "b")) == RepositoryPath.PathResult.Correct, string.Concat("Invalid path char: ", c)); } }
public override void Execute(ExecutionContext context) { context.Console.Write("Starting renaming... "); try { if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(NewName)) { throw new PackagingException("Invalid parameters"); } var name = ResolveVariable(NewName, context); var contextPath = ResolveRepositoryPath(Source, context); if (RepositoryPath.IsValidPath(contextPath) == RepositoryPath.PathResult.Correct) { context.AssertRepositoryStarted(); var content = Content.Load(contextPath); if (content == null) { throw new ContentNotFoundException(contextPath); } content["Name"] = name; content.SaveSameVersion(); return; } // it is not a repository path so it should be a file system path if (SourceIsRelativeTo == PathRelativeTo.Package) { contextPath = ResolvePackagePath(Source, context); var targetPath = Path.Combine(Path.GetDirectoryName(contextPath), name); File.Move(contextPath, targetPath); } else { var sourcePaths = ResolveAllTargets(Source, context); for (int i = 0; i < sourcePaths.Length; i++) { var targetPath = Path.Combine(Path.GetDirectoryName(sourcePaths[i]), name); File.Move(sourcePaths[i], targetPath); } } context.Console.WriteLine(name + " renamed all right."); } catch (InvalidStepParameterException) { Logger.LogMessage("Rename step can work with valid paths only."); throw; } }
public void Path_IsValid_WhiteSpaces() { // tab is invalid character. space is allowed, but not at beginning or end Assert.IsTrue(RepositoryPath.IsValidPath("/Root/ alma") == RepositoryPath.PathResult.StartsWithSpace, "#1"); Assert.IsTrue(RepositoryPath.IsValidPath("/Root/alma ") == RepositoryPath.PathResult.EndsWithSpace, "#2"); Assert.IsTrue(RepositoryPath.IsValidPath("/Root/ alma") == RepositoryPath.PathResult.InvalidPathChar, "#3"); // this is a TAB Assert.IsTrue(RepositoryPath.IsValidPath("/Root/alma ") == RepositoryPath.PathResult.InvalidPathChar, "#4"); // this is a TAB Assert.IsTrue(RepositoryPath.IsValidPath("/Root//alma") == RepositoryPath.PathResult.Empty, "#5"); Assert.IsTrue(RepositoryPath.IsValidPath("/Root/\t/alma") == RepositoryPath.PathResult.InvalidPathChar, "#6"); Assert.IsTrue(RepositoryPath.IsValidName(" alma") == RepositoryPath.PathResult.StartsWithSpace, "#7"); Assert.IsTrue(RepositoryPath.IsValidName("alma ") == RepositoryPath.PathResult.EndsWithSpace, "#8"); Assert.IsTrue(RepositoryPath.IsValidName(" alma") == RepositoryPath.PathResult.InvalidNameChar, "#9"); // this is a TAB Assert.IsTrue(RepositoryPath.IsValidName("alma ") == RepositoryPath.PathResult.InvalidNameChar, "#10"); // this is a TAB Assert.IsTrue(RepositoryPath.IsValidName(" ") == RepositoryPath.PathResult.StartsWithSpace, "#11"); Assert.IsTrue(RepositoryPath.IsValidName("\t") == RepositoryPath.PathResult.InvalidNameChar, "#12"); }
public override void Execute(ExecutionContext context) { context.AssertRepositoryStarted(); try { string sourcePath; if (SourceIsRelativeTo == PathRelativeTo.Package) { sourcePath = ResolvePackagePath(Source, context); } else { sourcePath = ResolveTargetPath(Source, context);// ResolveAllTargets(Source, context); } if (!IO.Directory.Exists(sourcePath) && !IO.File.Exists(sourcePath)) { throw new PackagingException(SR.Errors.Import.SourceNotFound + "\nSource value:" + sourcePath); } var checkResult = RepositoryPath.IsValidPath(Target); if (checkResult != RepositoryPath.PathResult.Correct) { if (!Target.StartsWith("/root", StringComparison.OrdinalIgnoreCase)) { throw new PackagingException(SR.Errors.Import.InvalidTarget, RepositoryPath.GetInvalidPathException(checkResult, Target)); } } if (!Node.Exists(Target)) { throw new PackagingException(SR.Errors.Import.TargetNotFound); } DoImport(null, sourcePath, Target); } catch (InvalidStepParameterException) { Logger.LogMessage("Import step can work with valid paths only."); throw; } }
public void Path_IsValidPath_PathMaxLengthCheck() { int maxPathLength = RepositoryPath.MaxLength; // Check at the limit first StringBuilder sb = new StringBuilder(maxPathLength + 1); sb.Append(RepositoryPath.PathSeparator); for (int i = RepositoryPath.PathSeparator.Length; i < maxPathLength; i++) { sb.Append("x"); } Assert.IsTrue(RepositoryPath.IsValidPath(sb.ToString()) == RepositoryPath.PathResult.Correct); // Add 1 char to the max limit sb.Append("x"); Assert.IsTrue(RepositoryPath.IsValidPath(sb.ToString()) == RepositoryPath.PathResult.TooLong); }
public override void Execute(ExecutionContext context) { if (string.IsNullOrEmpty(Path)) { throw new PackagingException(SR.Errors.InvalidParameters); } var path = (string)context.ResolveVariable(Path); // if Path refers to a content if (RepositoryPath.IsValidPath(path) == RepositoryPath.PathResult.Correct) { Logger.LogMessage(path); ExecuteOnContent(context); return; } // edit text files in the file system foreach (var targetPath in ResolvePaths(path, context).Where(File.Exists)) { Logger.LogMessage(targetPath); // we do not want to catch exceptions here: the step should fail in case of an error var text = File.ReadAllText(targetPath); text = Edit(text, context); // remove readonly flag from the file var fi = new FileInfo(targetPath); if (fi.IsReadOnly) { fi.IsReadOnly = false; } File.WriteAllText(targetPath, text); } }
/// <summary>Method that called by the packaging framework.</summary> public override void Execute(ExecutionContext context) { try { // Deleting a repository content if the given path is repository path var contextPath = ResolveRepositoryPath(Path, context); if (RepositoryPath.IsValidPath(contextPath) == RepositoryPath.PathResult.Correct) { context.AssertRepositoryStarted(); // Deleting a repository content if it exists if (Node.Exists(contextPath)) { Logger.LogMessage("Deleting content: " + contextPath); Node.ForceDelete(contextPath); } // Displaying a simple message. else { Logger.LogMessage("Content was not found: " + contextPath); } return; } // Getting local and network paths in one array. var paths = ResolveAllTargets(Path, context); // Executing deletion on all servers. foreach (var path in paths) { Execute(path); } } catch (InvalidStepParameterException) { Logger.LogMessage("Delete step can work with valid paths only."); throw; } }
public override void Execute(ExecutionContext context) { if (string.IsNullOrEmpty(Path) || string.IsNullOrEmpty(Template)) { throw new PackagingException(SR.Errors.InvalidParameters); } // if Path refers to a content if (RepositoryPath.IsValidPath(Path) == RepositoryPath.PathResult.Correct) { ExecuteOnContent(context); return; } // replace values in text files foreach (var targetPath in ResolveAllTargets(Path, context).Where(p => File.Exists(p))) { // we do not want to catch exceptions here: the step should fail in case of an error var text = File.ReadAllText(targetPath); text = text.Replace(Template, GetSource()); File.WriteAllText(targetPath, text); } }
/// <summary> /// Helper method for updating the given <see cref="Content"/> with a model represented by JObject. /// The <see cref="Content"/> will not be saved. /// </summary> /// <param name="content">The <see cref="Content"/> that will be modified. Cannot be null.</param> /// <param name="model">The modifier JObject instance. Cannot be null.</param> public static void UpdateFields(Content content, JObject model) { if (content == null) { throw new ArgumentNullException(nameof(content)); } if (model == null) { throw new ArgumentNullException(nameof(model)); } var isNew = content.Id == 0; foreach (var prop in model.Properties()) { if (string.IsNullOrEmpty(prop.Name) || prop.Name == "__ContentType" || prop.Name == "__ContentTemplate" || prop.Name == "Type" || prop.Name == "ContentType") { continue; } var hasField = content.Fields.TryGetValue(prop.Name, out var field); if (!hasField && content.SupportsAddingFieldsOnTheFly && (prop.Value as JValue)?.Value != null) { var value = ((JValue)prop.Value).Value; var fieldSetting = FieldSetting.InferFieldSettingFromType(value.GetType(), prop.Name); var meta = new FieldMetadata(true, true, prop.Name, prop.Name, fieldSetting); hasField = content.AddFieldsOnTheFly(new[] { meta }) && content.Fields.TryGetValue(prop.Name, out field); } if (hasField) { if (!field.ReadOnly) { if (prop.Value is JValue jvalue) { if (field is IntegerField) { field.SetData(Convert.ToInt32(jvalue.Value)); continue; } if (field is DateTimeField && jvalue.Value == null) { continue; } if (isNew && field is ReferenceField && jvalue.Value == null) { if (field.Name == "CreatedBy" || field.Name == "ModifiedBy") { continue; } } if (field is ReferenceField && jvalue.Value != null) { var refNode = jvalue.Type == JTokenType.Integer ? Node.LoadNode(Convert.ToInt32(jvalue.Value)) : Node.LoadNode(jvalue.Value.ToString()); field.SetData(refNode); continue; } if (isNew && field.Name == "Name" && jvalue.Value != null) { field.SetData(ContentNamingProvider.GetNameFromDisplayName(jvalue.Value.ToString())); continue; } field.SetData(jvalue.Value); continue; } if (prop.Value is JObject) { //TODO: ODATA: setting field when posted value is JObject. // field.SetData(jvalue.Value); continue; } if (prop.Value is JArray avalue) { if (field is ReferenceField) { var refValues = avalue.Values().ToList(); if (refValues.Count == 0) { field.SetData(null); continue; } var fsetting = field.FieldSetting as ReferenceFieldSetting; var nodes = refValues.Select(rv => rv.Type == JTokenType.Integer ? Node.LoadNode(Convert.ToInt32(rv.ToString())) : Node.LoadNode(rv.ToString())); if (fsetting?.AllowMultiple != null && fsetting.AllowMultiple.Value) { field.SetData(nodes); } else { field.SetData(nodes.First()); } } else if (field is ChoiceField) { // ChoiceField expects the value to be of type List<string> var list = new List <string>(); foreach (var token in avalue) { if (token is JValue value) { list.Add(value.Value.ToString()); } else { throw new Exception( $"Token type {token.GetType().Name} for field {field.Name} (type {field.GetType().Name}) is not supported."); } } field.SetData(list); } else if (field is AllowedChildTypesField && field.Name == "AllowedChildTypes" && content.ContentHandler is GenericContent gc) { var types = avalue.Values().Select(rv => { switch (rv.Type) { case JTokenType.Integer: return(Node.LoadNode(Convert.ToInt32(rv.ToString())) as ContentType); default: var typeId = rv.ToString(); if (RepositoryPath.IsValidPath(typeId) == RepositoryPath.PathResult.Correct) { return(Node.LoadNode(typeId) as ContentType); } return(ContentType.GetByName(typeId)); } }).Where(ct => ct != null).ToArray(); gc.SetAllowedChildTypes(types); } continue; } throw new SnNotSupportedException(); } } } }
/*========================================================== Install, Import, Export */ /// <summary> /// Parses the permission section (that usually comes from a .Content file in the file system) /// and imports all permission settings (including break and unbreak) into the security component. /// WARNING! Do not use this method in your code! /// </summary> public void ImportPermissions(XmlNode permissionsNode, string metadataPath) { Assert(PermissionType.SetPermissions); var permissionTypes = PermissionType.PermissionTypes; var aclEditor = _securityHandler.CreateAclEditor(); // parsing and executing 'Break' and 'Clear' var breakNode = permissionsNode.SelectSingleNode("Break"); var clearNode = permissionsNode.SelectSingleNode("Clear"); if (breakNode != null) { var convertToExplicit = clearNode == null; aclEditor.BreakInheritance(_node.Id, convertToExplicit ? new[] { EntryType.Normal } : new EntryType[0]); } else { aclEditor.UnbreakInheritance(_node.Id, new[] { EntryType.Normal }); } // executing 'Clear' if (clearNode != null) { aclEditor.RemoveExplicitEntries(_node.Id); } var identityElementIndex = 0; foreach (XmlElement identityElement in permissionsNode.SelectNodes("Identity")) { identityElementIndex++; // checking identity path var path = identityElement.GetAttribute("path"); var propagationAttr = identityElement.GetAttribute("propagation"); var localOnly = propagationAttr == null ? false : propagationAttr.ToLowerInvariant() == "localonly"; if (String.IsNullOrEmpty(path)) { throw ImportPermissionExceptionHelper(String.Concat("Missing or empty path attribute of the Identity element ", identityElementIndex, "."), metadataPath, null); } var pathCheck = RepositoryPath.IsValidPath(path); if (pathCheck != RepositoryPath.PathResult.Correct) { throw ImportPermissionExceptionHelper(String.Concat("Invalid path of the Identity element ", identityElementIndex, ": ", path, " (", pathCheck, ")."), metadataPath, null); } // getting identity node var identityNode = Node.LoadNode(path); if (identityNode == null) { SnLog.WriteWarning($"Identity {identityElementIndex} was not found: {path}."); continue; } // parsing value array foreach (XmlElement permissionElement in identityElement.SelectNodes("*")) { var permName = permissionElement.LocalName; var permType = permissionTypes.Where(p => String.Compare(p.Name, permName, true) == 0).FirstOrDefault(); if (permType == null) { throw ImportPermissionExceptionHelper(String.Concat("Permission type was not found in Identity ", identityElementIndex, "."), metadataPath, null); } switch (permissionElement.InnerText.ToLower()) { case "allow": case "allowed": aclEditor.Allow(_node.Id, identityNode.Id, localOnly, permType); break; case "deny": case "denied": aclEditor.Deny(_node.Id, identityNode.Id, localOnly, permType); break; default: throw ImportPermissionExceptionHelper(String.Concat("Invalid permission value in Identity ", identityElementIndex, ": ", permissionElement.InnerText, ". Allowed values: Allowed, Denied"), metadataPath, null); } } } aclEditor.Apply(); }
public void Path_IsValidPath_InvalidRoot() { Assert.IsTrue(RepositoryPath.IsValidPath("root") == RepositoryPath.PathResult.InvalidFirstChar); }
public void Path_IsValidPath_Empty() { Assert.IsTrue(RepositoryPath.IsValidPath("") == RepositoryPath.PathResult.Empty); }
public void Path_IsValidPath_Valid() { Assert.IsTrue(RepositoryPath.IsValidPath("/Root folder/System folder/Sub.folder/File name.extension") == RepositoryPath.PathResult.Correct); }
public void Path_IsValidPath_Null() { RepositoryPath.IsValidPath(null); }
protected void ParseParameters(ExecutionContext context, out Content content, out Dictionary <string, string> fieldValues, out bool overwrite) { if (string.IsNullOrEmpty(Content)) { throw new PackagingException(SR.Errors.InvalidParameters); } var path = context.ResolveVariable(Content) as string; if (RepositoryPath.IsValidPath(path) != RepositoryPath.PathResult.Correct) { throw new PackagingException(SR.Errors.InvalidParameters); } content = ContentRepository.Content.Load(path); if (content == null) { throw new PackagingException("Content not found: " + path); } fieldValues = new Dictionary <string, string>(); // either Field or Fields should be filled, but not both if ((string.IsNullOrEmpty(Name) && string.IsNullOrEmpty(Fields)) || (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Fields))) { throw new PackagingException(SR.Errors.InvalidParameters); } if (!string.IsNullOrEmpty(Name)) { // simple syntax, single field definition var fieldName = context.ResolveVariable(Name) as string; if (string.IsNullOrEmpty(fieldName) || !content.Fields.ContainsKey(fieldName)) { throw new PackagingException($"Field '{fieldName}' not found on content {path}"); } fieldValues[fieldName] = context.ResolveVariable(Value) as string; } else { // complex syntax, multiple field values are provided var xDoc = new XmlDocument(); xDoc.LoadXml($"<Fields>{context.ResolveVariable(Fields) as string}</Fields>"); if (xDoc.DocumentElement != null) { foreach (XmlNode fieldNode in xDoc.DocumentElement.ChildNodes) { var fieldName = fieldNode?.Attributes?["name"]?.Value; if (string.IsNullOrEmpty(fieldName)) { throw new InvalidStepParameterException("Field name is missing."); } if (!content.Fields.ContainsKey(fieldName)) { throw new InvalidStepParameterException( $"Content {content.Path} does not have a field with the name {fieldName}."); } if (fieldNode.FirstChild == null || fieldNode.ChildNodes.Count > 1) { throw new InvalidStepParameterException("Incorrect field xml definition."); } fieldValues[fieldName] = fieldNode.FirstChild.InnerXml; } } } overwrite = ParseOverwrite(context); }