public virtual ActionResult PropertyItemHtml(object data, string propertyPath, int depth, string pathPrefix) { ViewData["propertyPath"] = (pathPrefix ?? "") + propertyPath; ViewData["addDepth"] = depth - 1; string parentPath = propertyPath.Contains(".") ? propertyPath.UpToLast(".") : ""; string propertyName = (propertyPath.Contains(".") ? propertyPath.LastAfter(".") : propertyPath).UpTo("["); Type parentType = ReflectionX.GetPropertyTypeByPath(data.GetType(), parentPath); IList list = ReflectionX.GetPropertyValueByPath(data, propertyPath, true) as IList; var listProp = ReflectionX.GetPropertyByPath(data.GetType(), propertyPath); Type listType = listProp.PropertyType; if (listType.GetType().IsArray) { list = (IList)Array.CreateInstance(ReflectionX.ElementType(listType), 1); list[0] = CreateInstance(listType.GetElementType()); } else { list = (IList)Activator.CreateInstance(listType); list.Add(CreateInstance(ReflectionX.ElementType(listType))); } ViewData["list"] = list; var metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForProperty(null, parentType, propertyName); ViewData["CollectionAdditionalValues"] = metadata.AdditionalValues; RouteData.DataTokens.Add("CancelProcessingHtml", true); return(PartialView(ConfigHelper.GetViewPath("LyniconPropertyItem.cshtml"), data)); }
protected void DoEditAction(object data, string editAction) { IList list; Type itemType; switch (editAction.UpTo("-")) { case "add": list = ReflectionX.GetPropertyValueByPath(data, editAction.After("-")) as IList; itemType = list.GetType().GetGenericArguments()[0]; if (list != null) { list.Add(CreateInstance(itemType)); } break; case "del": list = ReflectionX.GetPropertyValueByPath(data, editAction.After("-").UpToLast("[")) as IList; itemType = list.GetType().GetGenericArguments()[0]; if (list != null) { ModelState.Clear(); // templating system will take old values out of the ModelState unless you do this list.RemoveAt(int.Parse(editAction.LastAfter("[").UpTo("]"))); } break; } }
public IActionResult Rename(string dataType, string path, bool pathInSummary, string newPath, bool newPathInSummary, bool?isDelete) { Type contentType = ContentTypeHierarchy.GetContentType(dataType); if (contentType == null) { return(Content("No such type, remember to add 'Content' where necessary")); } int propMissing = 0; int updated = 0; // create a new ContentRepository so we can bypass any block to writing caused by existing data problems // on the global data api var repo = new ContentRepository(new CoreDataSourceFactory(LyniconSystem.Instance)); repo.BypassChangeProblems = true; foreach (ContentItem ci in repo.Get <ContentItem>(contentType, new Type[] { contentType }, iq => iq)) { JObject jObjFrom; if (pathInSummary) { jObjFrom = JObject.Parse(ci.Summary ?? "{}"); } else { jObjFrom = JObject.Parse(ci.Content ?? "{}"); } JObject jObjTo = null; bool doDelete = isDelete ?? false; bool wasComputed = false; if (!doDelete) { if (newPathInSummary) { jObjTo = JObject.Parse(ci.Summary ?? "{}"); } else { jObjTo = JObject.Parse(ci.Content ?? "{}"); } jObjTo.CopyPropertyFrom(newPath, jObjFrom, path); JProperty prop = jObjTo.PropertyByPath(path); // try to deserialize in case its a computed property if (!pathInSummary && newPathInSummary && prop == null) { object content = jObjFrom.ToObject(contentType); object val = ReflectionX.GetPropertyValueByPath(content, path); if (val != null) { string pName = newPath.Contains(".") ? newPath.LastAfter(".") : newPath; jObjTo.AddAtPath(newPath, new JProperty(pName, val)); wasComputed = true; } } if (pathInSummary == newPathInSummary) { if (prop != null) { prop.Remove(); updated++; } else { propMissing++; } } } if (pathInSummary != newPathInSummary || doDelete) // we need to update both summary and content { // remove the old path JProperty prop = jObjFrom.PropertyByPath(path); if (prop != null) { prop.Remove(); updated++; if (pathInSummary) { ci.Summary = jObjFrom.ToString(); } else { ci.Content = jObjFrom.ToString(); } } else if (wasComputed) { updated++; } else { propMissing++; } } if (!doDelete) { if (newPathInSummary) { ci.Summary = jObjTo.ToString(); } else { ci.Content = jObjTo.ToString(); } } repo.Set(new List <object> { ci }, new Dictionary <string, object>()); } return(Content(string.Format("{0} updated {1} had property missing", updated, propMissing))); }
public static void SetReferenceProperties(object o, List <ReferenceProperty> refProps) { object curr = o; string currLeafParent = ""; foreach (var refProp in refProps) { string path = refProp.PropertyPath; string leafParent = path.Contains(".") ? path.UpToLast(".") : ""; string leafProperty = path.Contains(".") ? path.LastAfter(".") : path; PropertyInfo pi = null; if (currLeafParent != leafParent) { curr = ReflectionX.GetPropertyValueByPath(o, leafParent); } if (curr != null) { pi = curr.GetType().GetProperty(leafProperty.UpTo("[")); } if (pi == null) { log.Debug("Tried to assign reference value to property " + path + " on " + o.GetType().FullName); continue; } if (typeof(Reference).IsAssignableFrom(pi.PropertyType)) { if (pi.PropertyType.IsGenericType) { pi.SetValue(curr, refProp.ToReference(pi.PropertyType.GetGenericArguments()[0])); } else { pi.SetValue(curr, refProp.ToReference()); } } else { bool found = false; string indexStr = leafProperty.After("[").UpTo("]"); int idx = -1; int.TryParse(indexStr, out idx); // Now look for the property to be an IList of References if (idx > -1) { foreach (Type interf in pi.PropertyType.GetInterfaces()) { if (interf.GetGenericTypeDefinition() == typeof(IList <>)) { found = true; Type elType = interf.GetGenericArguments()[0]; if (!typeof(Reference).IsAssignableFrom(elType)) { throw new Exception("Cannot insert references into list at " + path + " of element type " + elType.FullName); } Reference refForList = null; if (elType.IsGenericType) { refForList = refProp.ToReference(elType.GetGenericArguments()[0]); } else { refForList = refProp.ToReference(); } object refList = pi.GetValue(curr); if (refList == null) { refList = Activator.CreateInstance(pi.PropertyType); pi.SetValue(curr, refList); } int count = (int)refList.GetType().GetProperty("Count").GetValue(refList); if (count < idx) { throw new Exception("Reference index " + idx + " at path " + path + " longer than maximum " + (count - 1)); } if (count == idx) { refList.GetType().GetMethod("Add").Invoke(refList, new object[] { refForList }); } else { refList.GetType().GetProperty("Item").SetValue(refList, refForList, new object[] { idx }); } break; } } } if (!found) { throw new Exception("Trying to SetReferenceProperties on a non reference property " + path); } } } }
/// <summary> /// Starting from a list of addresses and optionally (or only) the containers at those addresses, fetch /// any containers necessary and any other containers required to supply redirected properties for them, /// obtain the contained content items and collate their properties, returning the content items at the /// addresses. /// </summary> /// <typeparam name="T">Type of content items to return</typeparam> /// <param name="startContainers">Initial list of containers if they are available</param> /// <param name="startAddresses">Initial list of addresses, which may be omitted and derived from containers</param> /// <returns>List of content items</returns> public IEnumerable <T> Collate <T>(IEnumerable <object> startContainers, IEnumerable <Address> startAddresses) where T : class { // place to store all the containers we have currently Dictionary <VersionedAddress, object> containers; ItemVersion containerCommonVersion; startAddresses = startAddresses ?? Enumerable.Empty <Address>(); (containers, containerCommonVersion) = ProcessContainers(startContainers); List <Address> fetchAddrs = startAddresses .Where(sa => !containers.Any(kvp => kvp.Key.Address == sa)).ToList(); List <IGrouping <Type, Address> > allStartAddressesByType = null; if (DoCollation) { allStartAddressesByType = fetchAddrs.Concat(containers.Keys) .GroupBy(a => a.Type) .ToList(); // Get all addresses for items to collate (startAddresses plus addresses from startContainers) foreach (var addrTypeG in allStartAddressesByType) { Type contentType = addrTypeG.Key; var rpsAttributes = contentType .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), true) .Cast <RedirectPropertySourceAttribute>() .ToList(); foreach (Address addr in addrTypeG) { fetchAddrs.AddRange(rpsAttributes .Select(attr => new Address(attr.ContentType ?? contentType, PathFunctions.Redirect(addr.GetAsContentPath(), attr.SourceDescriptor)))); } } fetchAddrs = fetchAddrs.Distinct().ToList(); } bool pushVersion = (startContainers != null && DoCollation); if (pushVersion) // Get containers in any version that might be relevant to a start container { System.Versions.PushState(VersioningMode.Specific, containerCommonVersion); } try { // Get all the containers for collation (if current version is not fully specified, may be multiple per address) foreach (var cont in System.Repository.Get(typeof(object), fetchAddrs)) { var va = new VersionedAddress(System, cont); if (containers.ContainsKey(va)) { log.Error("Duplicate versioned address in db: " + va.ToString()); } else { containers.Add(new VersionedAddress(new Address(cont), new ItemVersion(System, cont).Canonicalise()), cont); } } } finally { if (pushVersion) { System.Versions.PopState(); } } if (!DoCollation) { // just return the containers we have foreach (object cont in containers.Values) { yield return(cont as T); } yield break; } // Create a lookup by (non-versioned) address of all the containers we have var contLookup = containers.ToLookup(kvp => kvp.Key.Address.ToString(), kvp => kvp.Value); // We have the data, now collate it into the content from the startContainers foreach (var addrTypeG in allStartAddressesByType) { // Process all the start addresses (including those of the start containers) of a given type Type contentType = addrTypeG.Key; var rpsAttributes = contentType .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), true) .Cast <RedirectPropertySourceAttribute>() .ToList(); foreach (var addr in addrTypeG.Select(a => new Address(a.Type, a)).Distinct()) // convert a VersionedAddress to an Address if necessary { var primaryPath = addr.GetAsContentPath(); if (!contLookup.Contains(new Address(addr.Type, addr).ToString())) { continue; } foreach (var cont in contLookup[addr.ToString()]) { object primaryContent = cont; if (primaryContent is IContentContainer) { primaryContent = ((IContentContainer)primaryContent).GetContent(System.Extender); } foreach (var rpsAttribute in rpsAttributes) { var refAddress = new VersionedAddress( rpsAttribute.ContentType ?? contentType, PathFunctions.Redirect(primaryPath, rpsAttribute.SourceDescriptor), new ItemVersion(System, cont).Canonicalise() ); if (refAddress.Address == addr) // redirected to itself, ignore { continue; } object refItem = containers.ContainsKey(refAddress) ? containers[refAddress] : null; if (refItem is IContentContainer) { refItem = ((IContentContainer)refItem).GetContent(System.Extender); } if (refItem != null) { foreach (string propertyPath in rpsAttribute.PropertyPaths) { var toFromPaths = GetPaths(propertyPath); object val = ReflectionX.GetPropertyValueByPath(refItem, toFromPaths[1]); var piSet = ReflectionX.GetPropertyByPath(primaryContent.GetType(), toFromPaths[0]); piSet.SetValue(primaryContent, val); } } } yield return(primaryContent as T); } } } }
/// <summary> /// Starting from a list of addresses and optionally (or only) the containers at those addresses, fetch /// any containers necessary and any other containers required to supply redirected properties for them, /// obtain the contained content items and collate their properties, returning the content items at the /// addresses. /// </summary> /// <typeparam name="T">Type of content items to return</typeparam> /// <param name="startContainers">Initial list of containers if they are available</param> /// <param name="startAddresses">Initial list of addresses, which may be omitted and derived from containers</param> /// <returns>List of content items</returns> public IEnumerable <T> Collate <T>(IEnumerable <object> startContainers, IEnumerable <Address> startAddresses) where T : class { // place to store all the containers we have currently var containers = new Dictionary <VersionedAddress, object>(); ItemVersion containerCommonVersion = null; // Ensure we have the start addresses if (startContainers != null) { containers = startContainers.ToDictionary(sc => new VersionedAddress(sc), sc => sc); startAddresses = containers.Keys.Select(va => va.Address).Distinct().ToList(); containerCommonVersion = ItemVersion.LeastAbstractCommonVersion(containers.Keys.Select(va => va.Version)); } var fetchAddrs = startAddresses .GroupBy(a => a.Type.GetCustomAttributes <RedirectPropertySourceAttribute>()) .SelectMany(ag => ag.SelectMany(a => ag.Key .Select(attr => attr.Redirect(a)) .Concat(a))) .Distinct() .Except(containers.Keys.Select(va => va.Address)) .ToList(); bool pushVersion = (startContainers != null); if (pushVersion) // Get containers in any version that might be relevant to a start container { VersionManager.Instance.PushState(VersioningMode.Specific, containerCommonVersion); } try { // Get all the containers for collation (if current version is not fully specified, may be multiple per address) foreach (var cont in Repository.Instance.Get(typeof(object), fetchAddrs)) { var va = new VersionedAddress(cont); if (containers.ContainsKey(va)) { log.Error("Duplicate versioned address in db: " + va.ToString()); } else { containers.Add(new VersionedAddress(cont), cont); } } } finally { if (pushVersion) { VersionManager.Instance.PopState(); } } var contLookup = containers.ToLookup(kvp => kvp.Key.Address.ToString(), kvp => kvp.Value); if (startContainers == null) { startContainers = startAddresses.SelectMany(a => contLookup[a.ToString()]); } // We have the data, now collate it into the content from the startContainers foreach (var addrTypeG in startAddresses.GroupBy(a => a.Type)) { // Process all the start addresses of a given type Type contentType = addrTypeG.Key; var rpsAttributes = contentType .GetCustomAttributes(typeof(RedirectPropertySourceAttribute), false) .Cast <RedirectPropertySourceAttribute>() .ToList(); foreach (var addr in addrTypeG) { var primaryPath = addr.GetAsContentPath(); if (!contLookup.Contains(addr.ToString())) { continue; } foreach (var cont in contLookup[addr.ToString()]) { object primaryContent = cont; if (primaryContent is IContentContainer) { primaryContent = ((IContentContainer)primaryContent).GetContent(); } foreach (var rpsAttribute in rpsAttributes) { var refAddress = new VersionedAddress( rpsAttribute.ContentType ?? contentType, PathFunctions.Redirect(primaryPath, rpsAttribute.SourceDescriptor), new ItemVersion(cont) ); if (refAddress.Address == addr) // redirected to itself, ignore { continue; } object refItem = containers.ContainsKey(refAddress) ? containers[refAddress] : null; if (refItem is IContentContainer) { refItem = ((IContentContainer)refItem).GetContent(); } if (refItem != null) { foreach (string propertyPath in rpsAttribute.PropertyPaths) { var toFromPaths = GetPaths(propertyPath); object val = ReflectionX.GetPropertyValueByPath(refItem, toFromPaths[1]); var piSet = ReflectionX.GetPropertyByPath(primaryContent.GetType(), toFromPaths[0]); piSet.SetValue(primaryContent, val); } } } yield return(primaryContent as T); } } } }