private async Task <string> RelationshipTypeContentPicker( ContentPickerFieldSettings contentPickerFieldSettings, ISyncNameProvider syncNameProvider) { //todo: handle multiple types string pickedContentType = contentPickerFieldSettings.DisplayedContentTypes[0]; string?relationshipType = null; if (contentPickerFieldSettings.Hint != null) { Match match = _relationshipTypeRegex.Match(contentPickerFieldSettings.Hint); if (match.Success) { relationshipType = $"{match.Groups[1].Value}"; } } if (relationshipType == null) { relationshipType = await syncNameProvider !.RelationshipTypeDefault(pickedContentType); } return(relationshipType); }
public async Task AddRelationship(JObject contentItemField, IDescribeRelationshipsContext parentContext) { var describeContentItemHelper = _serviceProvider.GetRequiredService <IDescribeContentItemHelper>(); ContentPickerFieldSettings contentPickerFieldSettings = parentContext.ContentPartFieldDefinition !.GetSettings <ContentPickerFieldSettings>(); JArray?contentItemIdsJArray = (JArray?)contentItemField[ContentItemIdsKey]; if (contentItemIdsJArray?.HasValues == true) { string relationshipType = await RelationshipTypeContentPicker(contentPickerFieldSettings, parentContext.SyncNameProvider); var sourceNodeLabels = await parentContext.SyncNameProvider.NodeLabels(parentContext.ContentItem.ContentType); string pickedContentType = contentPickerFieldSettings.DisplayedContentTypes[0]; IEnumerable <string> destNodeLabels = await parentContext.SyncNameProvider.NodeLabels(pickedContentType); parentContext.AvailableRelationships.Add(new ContentItemRelationship(sourceNodeLabels, relationshipType, destNodeLabels)); //todo: do we need each child, or can we just have a hashset of types foreach (var nestedItem in contentItemIdsJArray) { await describeContentItemHelper.BuildRelationships(nestedItem.Value <string>(), parentContext); } } }
public ContentPickerFieldGraphSyncerAddSyncComponentsTestsBase() { Logger = A.Fake <ILogger <ContentPickerFieldGraphSyncer> >(); ContentFieldGraphSyncer = new ContentPickerFieldGraphSyncer( A.Fake <IServiceProvider>()); ContentPickerFieldSettings = A.Fake <ContentPickerFieldSettings>(); A.CallTo(() => ContentPartFieldDefinition.GetSettings <ContentPickerFieldSettings>()) .Returns(ContentPickerFieldSettings); }
private async Task AddSyncComponents(IGraphMergeContext context, JArray?contentItemIdsJArray = null) { ContentPickerFieldSettings contentPickerFieldSettings = context.ContentPartFieldDefinition !.GetSettings <ContentPickerFieldSettings>(); //todo: use pickedContentSyncNameProvider in RelationshipTypeContentPicker? string relationshipType = await RelationshipTypeContentPicker(contentPickerFieldSettings, context.SyncNameProvider); //todo: support multiple pickable content types string pickedContentType = contentPickerFieldSettings.DisplayedContentTypes[0]; ISyncNameProvider pickedContentSyncNameProvider = _serviceProvider.GetSyncNameProvider(pickedContentType); IEnumerable <string> destNodeLabels = await pickedContentSyncNameProvider.NodeLabels(); if (contentItemIdsJArray?.HasValues != true) { context.ReplaceRelationshipsCommand.RemoveAnyRelationshipsTo( relationshipType, destNodeLabels); return; } ContentItem[] foundDestinationContentItems = await GetContentItemsFromIds( contentItemIdsJArray, context.ContentManager, context.ContentItemVersion); if (context.ContentItemVersion.GraphReplicaSetName == GraphReplicaSetNames.Preview && foundDestinationContentItems.Count() != contentItemIdsJArray.Count) { throw new GraphSyncException( $"Missing picked content items. Looked for {string.Join(",", contentItemIdsJArray.Values<string?>())}. Found {string.Join(",", foundDestinationContentItems.Select(i => i.ContentItemId))}. Current merge node command: {context.MergeNodeCommand}."); } // if we're syncing to the published graph, some items may be draft only, // so it's valid to have less found content items than are picked //todo: we could also check when publishing and take into account how many we expect not to find as they are draft only IEnumerable <object> foundDestinationNodeIds = foundDestinationContentItems.Select(ci => GetNodeId(ci !, pickedContentSyncNameProvider, context.ContentItemVersion)); long ordinal = 0; foreach (var item in foundDestinationNodeIds) { context.ReplaceRelationshipsCommand.AddRelationshipsTo( relationshipType, ContentPickerRelationshipProperties.Concat(new[] { new KeyValuePair <string, object>("Ordinal", ordinal++) }), destNodeLabels, pickedContentSyncNameProvider.IdPropertyName(), item); } }
public void GetNavigation(NavigationBuilder builder) { var workContext = _workContextAccessor.GetContext(); var httpContext = workContext.HttpContext; if (httpContext == null) { return; } var queryString = workContext.HttpContext.Request.QueryString; string part = queryString["part"]; string field = queryString["field"]; ContentPickerFieldSettings settings = null; // if the picker is loaded for a specific field, apply custom settings if (!String.IsNullOrEmpty(part) && !String.IsNullOrEmpty(field)) { var definition = _contentDefinitionManager.GetPartDefinition(part).Fields.FirstOrDefault(x => x.Name == field); if (definition != null) { settings = definition.Settings.GetModel <ContentPickerFieldSettings>(); } } if (settings != null && !settings.ShowContentTab) { return; } builder.Add(T("Content Picker"), menu => menu .Add(T("Recent Content"), "5", item => item.Action("Index", "Admin", new { area = "Orchard.ContentPicker" }).LocalNav())); }
public async Task <(bool validated, string failureReason)> ValidateSyncComponent( JObject contentItemField, IValidateAndRepairContext context) { ContentPickerFieldSettings contentPickerFieldSettings = context.ContentPartFieldDefinition !.GetSettings <ContentPickerFieldSettings>(); string relationshipType = await RelationshipTypeContentPicker(contentPickerFieldSettings, context.SyncNameProvider); IRelationship[] actualRelationships = context.NodeWithRelationships.OutgoingRelationships .Where(r => r.Type == relationshipType) .ToArray(); var contentItemIds = (JArray)contentItemField[ContentItemIdsKey] !; ContentItem[] destinationContentItems = await GetContentItemsFromIds( contentItemIds, context.ContentManager, context.ContentItemVersion); //todo: separate check for missing items, before check relationships //todo: move into helper? //todo: have to allow for published picker referencing draft item if (destinationContentItems.Count() != actualRelationships.Length) { return(false, $"expecting {destinationContentItems.Count()} relationships of type {relationshipType} in graph, but found {actualRelationships.Length}"); } long ordinal = 0; foreach (ContentItem destinationContentItem in destinationContentItems) { //todo: should logically be called using destination ContentType, but it makes no difference atm object destinationId = context.SyncNameProvider.GetNodeIdPropertyValue( destinationContentItem.Content.GraphSyncPart, context.ContentItemVersion); string destinationIdPropertyName = context.SyncNameProvider.IdPropertyName(destinationContentItem.ContentType); var expectedRelationshipProperties = ContentPickerRelationshipProperties.Concat( new[] { new KeyValuePair <string, object>("Ordinal", ordinal++) }); //todo: we might want to check that all the supplied relationship properties are there, // whilst not failing validation if other properties are present? (bool validated, string failureReason) = context.GraphValidationHelper.ValidateOutgoingRelationship( context.NodeWithRelationships, relationshipType, destinationIdPropertyName, destinationId, expectedRelationshipProperties); if (!validated) { return(false, failureReason); } // keep a count of how many relationships of a type we expect to be in the graph context.ExpectedRelationshipCounts.IncreaseCount(relationshipType); } return(true, ""); }
public ActionResult Index(ListContentsViewModel model, PagerParameters pagerParameters, string part, string field, string types) { var menuItems = _navigationManager.BuildMenu("content-picker").ToList(); var contentPickerMenuItem = menuItems.FirstOrDefault(); if (contentPickerMenuItem == null) { return(HttpNotFound()); } if (contentPickerMenuItem.Items.All(x => x.Text.TextHint != "Recent Content")) { // the default tab should not be displayed, redirect to the next one var root = menuItems.FirstOrDefault(); if (root == null) { return(HttpNotFound()); } var firstChild = root.Items.First(); if (firstChild == null) { return(HttpNotFound()); } var routeData = new RouteValueDictionary(firstChild.RouteValues); var queryString = Request.QueryString; foreach (var key in queryString.AllKeys) { if (!String.IsNullOrEmpty(key)) { routeData[key] = queryString[key]; } } return(RedirectToRoute(routeData)); } ContentPickerFieldSettings settings = null; // if the picker is loaded for a specific field, apply custom settings if (!String.IsNullOrEmpty(part) && !String.IsNullOrEmpty(field)) { var definition = _contentDefinitionManager.GetPartDefinition(part).Fields.FirstOrDefault(x => x.Name == field); if (definition != null) { settings = definition.Settings.GetModel <ContentPickerFieldSettings>(); } } if (settings != null && !String.IsNullOrEmpty(settings.DisplayedContentTypes)) { types = settings.DisplayedContentTypes; } IEnumerable <ContentTypeDefinition> contentTypes; if (!String.IsNullOrEmpty(types)) { var rawTypes = types.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); contentTypes = _contentDefinitionManager .ListTypeDefinitions() .Where(x => x.Parts.Any(p => rawTypes.Contains(p.PartDefinition.Name)) || rawTypes.Contains(x.Name)) .ToArray(); } else { contentTypes = GetCreatableTypes(false).ToList(); } var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); var query = Services.ContentManager.Query(VersionOptions.Latest, contentTypes.Select(ctd => ctd.Name).ToArray()); if (!string.IsNullOrEmpty(model.Options.SelectedFilter)) { var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.Options.SelectedFilter); if (contentTypeDefinition == null) { return(HttpNotFound()); } model.TypeDisplayName = !string.IsNullOrWhiteSpace(contentTypeDefinition.DisplayName) ? contentTypeDefinition.DisplayName : contentTypeDefinition.Name; query = query.ForType(model.Options.SelectedFilter); } switch (model.Options.OrderBy) { case ContentsOrder.Modified: query = query.OrderByDescending <CommonPartRecord>(cr => cr.ModifiedUtc); break; case ContentsOrder.Published: query = query.OrderByDescending <CommonPartRecord>(cr => cr.PublishedUtc); break; case ContentsOrder.Created: query = query.OrderByDescending <CommonPartRecord>(cr => cr.CreatedUtc); break; } model.Options.FilterOptions = contentTypes .Select(ctd => new KeyValuePair <string, string>(ctd.Name, ctd.DisplayName)) .ToList().OrderBy(kvp => kvp.Value); var pagerShape = Services.New.Pager(pager).TotalItemCount(query.Count()); var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList(); var list = Services.New.List(); list.AddRange(pageOfContentItems.Select(ci => Services.ContentManager.BuildDisplay(ci, "SummaryAdmin"))); foreach (IShape item in list.Items) { item.Metadata.Type = "ContentPicker"; } var tab = Services.New.RecentContentTab() .ContentItems(list) .Pager(pagerShape) .Options(model.Options) .TypeDisplayName(model.TypeDisplayName ?? ""); // retain the parameter in the pager links RouteData.Values["Options.SelectedFilter"] = model.Options.SelectedFilter; RouteData.Values["Options.OrderBy"] = model.Options.OrderBy.ToString(); RouteData.Values["Options.ContentsStatus"] = model.Options.ContentsStatus.ToString(); return(new ShapeResult(this, Services.New.ContentPicker().Tab(tab))); }