/// <inheritdoc /> public override Topic Load(int topicId, bool isRecursive = true) { /*------------------------------------------------------------------------------------------------------------------------ | Establish database connection \-----------------------------------------------------------------------------------------------------------------------*/ var topic = (Topic?)null; using var connection = new SqlConnection(_connectionString); using var command = new SqlCommand("GetTopics", connection) { CommandType = CommandType.StoredProcedure, CommandTimeout = 120 }; /*------------------------------------------------------------------------------------------------------------------------ | Establish query parameters \-----------------------------------------------------------------------------------------------------------------------*/ command.AddParameter("TopicID", topicId); command.AddParameter("DeepLoad", isRecursive); /*------------------------------------------------------------------------------------------------------------------------ | Process database query \-----------------------------------------------------------------------------------------------------------------------*/ try { connection.Open(); using var reader = command.ExecuteReader(); topic = reader.LoadTopicGraph(); } /*------------------------------------------------------------------------------------------------------------------------ | Catch exception \-----------------------------------------------------------------------------------------------------------------------*/ catch (SqlException exception) { throw new TopicRepositoryException($"Topics failed to load: '{exception.Message}'", exception); } /*------------------------------------------------------------------------------------------------------------------------ | Validate results \-----------------------------------------------------------------------------------------------------------------------*/ if (topic is null) { if (topicId == -1) { topic = TopicFactory.Create("Root", "Container"); } else { throw new TopicNotFoundException(topicId); } } /*------------------------------------------------------------------------------------------------------------------------ | Establish content type cache >------------------------------------------------------------------------------------------------------------------------- | If this load represents the entire topic graph, then relay the content type configuration to the TopicRepositoryBase in | order to either update or establish the content type cache. Not only does this prevent the need for a separate redundant | call later but, even more importantly, it helps ensure the same object references are maintained so that any updates to | subsequently cached content types are available. \-----------------------------------------------------------------------------------------------------------------------*/ base.SetContentTypeDescriptors(topic); /*------------------------------------------------------------------------------------------------------------------------ | Return objects \-----------------------------------------------------------------------------------------------------------------------*/ return topic; }
public void SetValue_CorrectValue_IsReturned() { var topic = TopicFactory.Create("Test", "Container"); topic.Attributes.SetValue("Foo", "Bar"); Assert.AreEqual<string>("Bar", topic.Attributes.GetValue("Foo")); }
public void Create_ContentType_ReturnsDerivedTopic() { var topic = TopicFactory.Create("Test", "ContentTypeDescriptor"); Assert.IsNotNull(topic); Assert.IsInstanceOfType(topic, typeof(ContentTypeDescriptor)); }
public void Create_ReturnsTopic() { var topic = TopicFactory.Create("Test", "Page"); Assert.IsNotNull(topic); Assert.AreEqual<string>(topic.Key, "Test"); Assert.AreEqual<string>(topic.Attributes.GetValue("ContentType"), "Page"); }
/*========================================================================================================================== | METHOD: CREATE FAKE DATA \-------------------------------------------------------------------------------------------------------------------------*/ /// <summary> /// Creates a collection of fake data that loosely mimics a bare bones database. /// </summary> private Topic CreateFakeData() { /*------------------------------------------------------------------------------------------------------------------------ | Establish root \-----------------------------------------------------------------------------------------------------------------------*/ var rootTopic = TopicFactory.Create("Root", "Container", 900); var currentAttributeId = 800; /*------------------------------------------------------------------------------------------------------------------------ | Establish configuration \-----------------------------------------------------------------------------------------------------------------------*/ var configuration = TopicFactory.Create("Configuration", "Container", rootTopic); var contentTypes = TopicFactory.Create("ContentTypes", "ContentTypeDescriptor", configuration); addAttribute(contentTypes, "Key", "TextAttribute", false, true); addAttribute(contentTypes, "ContentType", "TextAttribute", false, true); addAttribute(contentTypes, "Title", "TextAttribute", true, true); addAttribute(contentTypes, "TopicId", "TopicReferenceAttribute", false); var contentTypeDescriptor = TopicFactory.Create("ContentTypeDescriptor", "ContentTypeDescriptor", contentTypes); addAttribute(contentTypeDescriptor, "ContentTypes", "RelationshipAttribute"); addAttribute(contentTypeDescriptor, "Attributes", "NestedTopicListAttribute"); TopicFactory.Create("Container", "ContentTypeDescriptor", contentTypes); TopicFactory.Create("Lookup", "ContentTypeDescriptor", contentTypes); TopicFactory.Create("LookupListItem", "ContentTypeDescriptor", contentTypes); TopicFactory.Create("List", "ContentTypeDescriptor", contentTypes); var attributeDescriptor = (ContentTypeDescriptor)TopicFactory.Create("AttributeDescriptor", "ContentTypeDescriptor", contentTypes); addAttribute(attributeDescriptor, "DefaultValue", "TextAttribute", false, true); addAttribute(attributeDescriptor, "IsRequired", "TextAttribute", false, true); TopicFactory.Create("BooleanAttribute", "ContentTypeDescriptor", attributeDescriptor); TopicFactory.Create("NestedTopicListAttribute", "ContentTypeDescriptor", attributeDescriptor); TopicFactory.Create("NumberAttribute", "ContentTypeDescriptor", attributeDescriptor); TopicFactory.Create("RelationshipAttribute", "ContentTypeDescriptor", attributeDescriptor); TopicFactory.Create("TextAttribute", "ContentTypeDescriptor", attributeDescriptor); TopicFactory.Create("TopicReferenceAttribute", "ContentTypeDescriptor", attributeDescriptor); var pageContentType = TopicFactory.Create("Page", "ContentTypeDescriptor", contentTypes); addAttribute(pageContentType, "MetaTitle"); addAttribute(pageContentType, "MetaDescription"); addAttribute(pageContentType, "IsHidden", "TextAttribute", false); addAttribute(pageContentType, "TopicReference", "TopicReferenceAttribute", false); pageContentType.Relationships.SetTopic("ContentTypes", pageContentType); pageContentType.Relationships.SetTopic("ContentTypes", contentTypeDescriptor); var contactContentType = TopicFactory.Create("Contact", "ContentTypeDescriptor", contentTypes); addAttribute(contactContentType, "Name", isExtended: false); addAttribute(contactContentType, "AlternateEmail", isExtended: false); addAttribute(contactContentType, "BillingContactEmail", isExtended: false); /*------------------------------------------------------------------------------------------------------------------------ | Local addAttribute() helper function \-----------------------------------------------------------------------------------------------------------------------*/ AttributeDescriptor addAttribute( Topic contentType, string attributeKey, string editorType = "TextAttribute", bool isExtended = true, bool isRequired = false ) { var container = contentType.Children.GetTopic("Attributes"); if (container is null) { container = TopicFactory.Create("Attributes", "List", contentType); container.Attributes.SetBoolean("IsHidden", true); } var attribute = (AttributeDescriptor)TopicFactory.Create(attributeKey, editorType, currentAttributeId++, container); attribute.IsRequired = isRequired; attribute.IsExtendedAttribute = isExtended; return attribute; } /*------------------------------------------------------------------------------------------------------------------------ | Establish metadata \-----------------------------------------------------------------------------------------------------------------------*/ var metadata = TopicFactory.Create("Metadata", "Container", configuration); var categories = TopicFactory.Create("Categories", "Lookup", metadata); var lookup = TopicFactory.Create("LookupList", "List", categories); for (var i=1; i<=5; i++) { TopicFactory.Create("Category" + i, "LookupListItem", lookup); } /*------------------------------------------------------------------------------------------------------------------------ | Establish content \-----------------------------------------------------------------------------------------------------------------------*/ var web = TopicFactory.Create("Web", "Page", 10000, rootTopic); CreateFakeData(web, 2, 3); var pageGroup = TopicFactory.Create("Web_3", "PageGroup", web); TopicFactory.Create("Web_3_0", "Page", pageGroup); TopicFactory.Create("Web_3_1", "Page", pageGroup); /*------------------------------------------------------------------------------------------------------------------------ | Set to cache \-----------------------------------------------------------------------------------------------------------------------*/ return rootTopic; }
/*========================================================================================================================== | METHOD: INVOKE \-------------------------------------------------------------------------------------------------------------------------*/ /// <summary> /// Assembles the view model for the <see cref="TopicListViewComponent"/>. /// </summary> public IViewComponentResult Invoke( EditingTopicViewModel currentTopic, TopicListAttributeDescriptorViewModel attribute, string? htmlFieldPrefix = null ) { /*------------------------------------------------------------------------------------------------------------------------ | Validate parameters \-----------------------------------------------------------------------------------------------------------------------*/ Contract.Requires(currentTopic, nameof(currentTopic)); Contract.Requires(attribute, nameof(attribute)); Contract.Requires(currentTopic.ContentType, nameof(currentTopic.ContentType)); Contract.Requires(attribute.Key, nameof(attribute.Key)); /*------------------------------------------------------------------------------------------------------------------------ | Set HTML prefix \-----------------------------------------------------------------------------------------------------------------------*/ ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix; /*------------------------------------------------------------------------------------------------------------------------ | Establish view model \-----------------------------------------------------------------------------------------------------------------------*/ var viewModel = new TopicListAttributeViewModel(currentTopic, attribute); /*------------------------------------------------------------------------------------------------------------------------ | Set default value \-----------------------------------------------------------------------------------------------------------------------*/ var defaultValue = currentTopic.Attributes.ContainsKey(attribute.Key)? currentTopic.Attributes[attribute.Key] : null; /*------------------------------------------------------------------------------------------------------------------------ | Get root topic \-----------------------------------------------------------------------------------------------------------------------*/ var rootTopic = (Topic?)null; if (attribute.RelativeTopicBase is not null) { var baseTopic = _topicRepository.Load(currentTopic.UniqueKey); Contract.Assume(baseTopic, $"The topic with the key '{currentTopic.UniqueKey}' could not be located."); if (String.IsNullOrEmpty(currentTopic.Key)) { baseTopic = TopicFactory.Create("NewTopic", currentTopic.ContentType, baseTopic); baseTopic.Parent?.Children.Remove(baseTopic); } rootTopic = attribute.RelativeTopicBase switch { "CurrentTopic" => baseTopic, "ParentTopic" => baseTopic.Parent, "GrandparentTopic" => (Topic?)baseTopic.Parent?.Parent, "ContentTypeDescriptor" => (Topic?)_topicRepository.GetContentTypeDescriptors().FirstOrDefault(t => t.Key.Equals(baseTopic.ContentType, StringComparison.Ordinal)), _ => baseTopic }; } else if (attribute.RootTopic is not null) { rootTopic = _topicRepository.Load(attribute.RootTopic.UniqueKey); } if (rootTopic is not null && !String.IsNullOrEmpty(attribute.RelativeTopicPath)) { rootTopic = rootTopic.GetByUniqueKey(rootTopic.GetUniqueKey() + ":" + attribute.RelativeTopicPath); } /*------------------------------------------------------------------------------------------------------------------------ | Get values \-----------------------------------------------------------------------------------------------------------------------*/ var topics = GetTopics( rootTopic, attribute.AttributeKey, attribute.AttributeValue ); /*------------------------------------------------------------------------------------------------------------------------ | Set label \-----------------------------------------------------------------------------------------------------------------------*/ if (!String.IsNullOrEmpty(viewModel.InheritedValue)) { setLabel(viewModel.InheritedValue, "inherited value"); } else if (!String.IsNullOrEmpty(viewModel.AttributeDescriptor.DefaultValue)) { setLabel(viewModel.AttributeDescriptor.DefaultValue, "default value"); } else if (!String.IsNullOrEmpty(viewModel.AttributeDescriptor.ImplicitValue)) { setLabel(viewModel.AttributeDescriptor.ImplicitValue, "implicit default"); } else { setLabel(attribute.DefaultLabel?? "Select an option…"); } /*------------------------------------------------------------------------------------------------------------------------ | Get values from repository \-----------------------------------------------------------------------------------------------------------------------*/ foreach (var topic in topics) { var title = viewModel.TopicList.Any(t => t.Text == topic.Title)? $"{topic.Title} ({topic.Key})" : topic.Title; var value = getValue(topic); viewModel.TopicList.Add( new() { Value = value, Text = title, Selected = value == defaultValue } ); } /*------------------------------------------------------------------------------------------------------------------------ | Return view with view model \-----------------------------------------------------------------------------------------------------------------------*/ return View(viewModel); /*------------------------------------------------------------------------------------------------------------------------ | Function: Get Value \-----------------------------------------------------------------------------------------------------------------------*/ string getValue(QueryResultTopicViewModel topic) => ReplaceTokens(topic, "{" + attribute.ValueProperty + "}"); /*------------------------------------------------------------------------------------------------------------------------ | Function: Set Label \-----------------------------------------------------------------------------------------------------------------------*/ void setLabel(string value, string? contextualLabel = null) { var inheritedTopic = topics.Where(t => t.Key == value).FirstOrDefault(); var label = inheritedTopic?.Title ?? value; if (contextualLabel is not null) { label += " (" + contextualLabel + ")"; } viewModel?.TopicList.Add( new() { Value = "", Text = label } ); } }
public void DrinkTopicFactory_Create_DrinkTopic() { Topic expected = _topicFactory.Create(A.Dummy <SimpleDrink>(), A.Dummy <TopicInfo>()); Assert.NotNull(expected); }