Example #1
0
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="RelationshipViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      RelationshipAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));
      Contract.Requires(attribute.Key, nameof(attribute.Key));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      currentTopic.Attributes.TryGetValue(attribute.Key, out var value);

      var model = new AttributeViewModel<RelationshipAttributeDescriptorViewModel>(currentTopic, attribute) {
        Value                   = CleanArray(value)
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="TextAreaViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      TextAreaAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel = new AttributeViewModel<TextAreaAttributeDescriptorViewModel>(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

    }
Example #3
0
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="TokenizedTopicListViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      TokenizedTopicListAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var model = new TokenizedTopicListAttributeViewModel(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Set model values
      \-----------------------------------------------------------------------------------------------------------------------*/
      model.SelectedTopics = GetSelectedTopics(model.Value);

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }
Example #4
0
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="FilePathViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      FilePathAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var model = new FilePathAttributeViewModel(currentTopic, attribute) {
        InheritedValue = GetInheritedValue(attribute.Key!, attribute)
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Set model values
      \-----------------------------------------------------------------------------------------------------------------------*/

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }
Example #5
0
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="LastModifiedByViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      LastModifiedByAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));
      Contract.Requires(attribute.Key, nameof(attribute.Key));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      currentTopic.Attributes.TryGetValue(attribute.Key, out var value);

      var model                 = new LastModifiedByAttributeViewModel(currentTopic, attribute) {
        CurrentValue            = value,
        Value                   = HttpContext.User.Identity.Name
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }
Example #6
0
 /*==========================================================================================================================
 | METHOD: INVOKE
 \-------------------------------------------------------------------------------------------------------------------------*/
 /// <summary>
 ///   Assembles the view model for the <see cref="InstructionViewComponent"/>.
 /// </summary>
 public IViewComponentResult Invoke(
   EditingTopicViewModel currentTopic,
   InstructionAttributeDescriptorViewModel attribute,
   string htmlFieldPrefix
 ) {
   ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;
   return View(new AttributeViewModel<InstructionAttributeDescriptorViewModel>(currentTopic, attribute));
 }
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="NestedTopicListViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      NestedTopicListAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));
      Contract.Requires(attribute.Key, nameof(attribute.Key));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel = new NestedTopicListAttributeViewModel(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Set model values
      \-----------------------------------------------------------------------------------------------------------------------*/
      if (HttpContext.Request.Query.TryGetValue("IsNew", out var action)) {
        viewModel.IsNew         = action.FirstOrDefault().Equals("true", StringComparison.OrdinalIgnoreCase);
      }
      viewModel.UniqueKey       = currentTopic.UniqueKey;
      viewModel.WebPath         = currentTopic.WebPath;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish nested topic container, if needed
      \-----------------------------------------------------------------------------------------------------------------------*/
      if (!viewModel.IsNew) {
        var topic = _topicRepository.Load(viewModel.UniqueKey);
        Contract.Assume(topic, $"The topic with the unique key '{viewModel.UniqueKey}' could not be found.");
        if (!topic.Children.Contains(attribute.Key)) {
          var topicContainer = TopicFactory.Create(attribute.Key, "List", topic);
          _topicRepository.Save(topicContainer);
        }
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

    }
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="TopicReferenceViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      TopicReferenceAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Set configuration values
      \-----------------------------------------------------------------------------------------------------------------------*/
      //### TODO 6.0.0: Remove hard-coded reference to BaseTopic; this is only needed for backward compatibility with 5.0.0.
      var isBaseTopic           = attribute.Key.Equals("BaseTopic", StringComparison.OrdinalIgnoreCase);
      if (attribute.UseCurrentContentType || isBaseTopic) {
        attribute = attribute with {
          AttributeKey          = "ContentType",
          AttributeValue        = currentTopic.ContentType,
          AutoPostBack          = isBaseTopic? true : attribute.AutoPostBack
        };
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel = new AttributeViewModel<TopicReferenceAttributeDescriptorViewModel>(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

    }

  } // Class
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="LastModifiedViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      LastModifiedAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));
      Contract.Requires(attribute.Key, nameof(attribute.Key));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Set model defaults
      \-----------------------------------------------------------------------------------------------------------------------*/
      currentTopic.Attributes.TryGetValue(attribute.Key, out var currentValue);
      if (currentTopic.LastModified != DateTime.MinValue) {
        currentValue            = currentTopic.LastModified.ToString(CultureInfo.InvariantCulture);
      }
      var value                 = DateTime.Now.ToString(CultureInfo.InvariantCulture);

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var model = new LastModifiedAttributeViewModel(currentTopic, attribute) {
        CurrentValue            = currentValue,
        Value                   = value
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="HtmlViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      HtmlAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Set configuration values
      \-----------------------------------------------------------------------------------------------------------------------*/
      if (attribute.Height is null || attribute.Height is 0 && attribute.Rows is not null) {
        attribute = attribute with {
          Height = attribute.Rows * 20
        };
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel = new AttributeViewModel<HtmlAttributeDescriptorViewModel>(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

    }

  } // Class
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="NumberViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      NumberAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel = new AttributeViewModel<NumberAttributeDescriptorViewModel>(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

    }
Example #12
0
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="FileListViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      FileListAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var model                 = new FileListAttributeViewModel(currentTopic, attribute) {
        AbsolutePath            = _webHostEnvironment.ContentRootPath + attribute.Path
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Set model values
      \-----------------------------------------------------------------------------------------------------------------------*/
      foreach (var file in GetFiles(model)) {
        model.Files.Add(file);
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="ContentTypeListViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      ContentTypeListAttributeDescriptorViewModel? attributeDescriptor = null,
      IEnumerable<PermittedContentTypeViewModel>? values = null,
      string? onModalClose = null
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel             = new ContentTypeListViewModel() {
        CurrentTopic            = currentTopic,
        AttributeKey            = attributeDescriptor?.Key,
        EnableModal             = attributeDescriptor?.EnableModal?? false,
        OnModalClose            = onModalClose
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Set label
      \-----------------------------------------------------------------------------------------------------------------------*/
      viewModel.TopicList.Add(
        new() {
          Value = null,
          Text = "Add a child topic…"
        }
      );

      /*------------------------------------------------------------------------------------------------------------------------
      | Add values to view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      foreach (var contentType in values.OrderBy(c => c.Title)) {
        viewModel.TopicList.Add(
          new() {
            Value               = getValue(contentType.Key!),
            Text                = contentType.Title
          }
        );
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Get content type
      >-------------------------------------------------------------------------------------------------------------------------
      | If the database is uninitialized, the content type won't be found. In that case, return an empty view, which will
      | effectively hide the component. If the topic cannot be found, assume it is a new topic and attempt to load the parent
      | for context.
      \-----------------------------------------------------------------------------------------------------------------------*/
      var contentTypes          = _topicRepository.GetContentTypeDescriptors();
      var actualTopic           = _topicRepository.Load(currentTopic.Id)?? _topicRepository.Load(currentTopic.Parent?.Id?? Int32.MinValue);
      var actualContentType     = contentTypes.GetValue(currentTopic.ContentType!);

      if (actualContentType is null || actualTopic is null) {
        return View(viewModel);
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Get permitted content types for container
      >-------------------------------------------------------------------------------------------------------------------------
      | Containers provide special rules allowing the permitted content types to be defined—or even expanded—on the topic
      | instance itself, instead of exclusively on the content type descriptor. This is useful since often containers are meant
      | to organize a specific type of content. For example, a Container called "Forms" might be used exclusively to organized
      | Form topics.
      \-----------------------------------------------------------------------------------------------------------------------*/
      if (actualContentType.Key.Equals("Container", StringComparison.OrdinalIgnoreCase)) {
        var permittedContentTypes = actualTopic
          .Relationships
          .GetValues("ContentTypes")
          .Select(c =>
            new SelectListItem() {
              Value             = getValue(c.Key),
              Text              = c.Title
            }
          );
        foreach (var contentType in permittedContentTypes) {
          viewModel.TopicList.Add(contentType);
        }
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Get implicit values
      >-------------------------------------------------------------------------------------------------------------------------
      | If no permitted content types are explicitly defined on the content type—or the topic, in the case of a container—then
      | we instead load a generic list of content types. We don't want to display all content types, however, as many make
      | little sense outside of specific contexts. For instance, most content types that derive from Items only make sense as
      | nested topics. Similarly, specialized types like Home might only make sense in one location. To facilitate this, only
      | content types explicitly annotated as "implicitly permitted", and which are not marked as hidden are displayed. This
      | typically include the Page content type, and popular derivatives of it, such as Content List.
      \-----------------------------------------------------------------------------------------------------------------------*/
      if (viewModel.TopicList.Count is 1 && !actualContentType.DisableChildTopics) {

        var implicitValues = contentTypes
          .Where(c => actualContentType.Equals("Container") || c.Attributes.GetBoolean("ImplicitlyPermitted", false))
          .Where(c => !c.IsHidden)
          .OrderBy(c => c.Title)
          .Select(c =>
            new SelectListItem {
              Value             = getValue(c.Key),
              Text              = c.Title
            }
          );
        foreach (var contentType in implicitValues) {
          viewModel.TopicList.Add(contentType);
        }
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

      /*------------------------------------------------------------------------------------------------------------------------
      | Helper functions
      \-----------------------------------------------------------------------------------------------------------------------*/
      string getValue(string contentType) =>
        $"{Url.Action("Edit")}" +
        $"/{attributeDescriptor?.Key}" +
        $"?IsNew=true" +
        $"&IsModal={attributeDescriptor?.EnableModal?? false}" +
        $"&ContentType={contentType}";

    }
Example #14
0
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="ReflexiveViewComponent"/>.
    /// </summary>
    public async Task<IViewComponentResult> InvokeAsync(
      EditingTopicViewModel currentTopic,
      ReflexiveAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish snapshot of previously saved attribute descriptor
      \-----------------------------------------------------------------------------------------------------------------------*/
      var topic                 = _topicRepository.Load(currentTopic.UniqueKey);
      var reflexiveViewModel    = (AttributeDescriptorViewModel?)null;

      if (topic?.ContentType.EndsWith("AttributeDescriptor", StringComparison.OrdinalIgnoreCase)?? false) {
        reflexiveViewModel      = (AttributeDescriptorViewModel?)await _topicMappingService.MapAsync(topic).ConfigureAwait(false);
      }

      if (reflexiveViewModel is null) {
        reflexiveViewModel      = new();
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish hybrid view model
      >-------------------------------------------------------------------------------------------------------------------------
      | The ParentAttributeDescriptor will be of the target type expected for the view component that will be executed. But it
      | should use the core AttributeDescriptor attributes of the current attribute, so it shows up in the same location with
      | the same title and description as defined for the ReflexiveAttributeDescriptorViewModel.
      \-----------------------------------------------------------------------------------------------------------------------*/
      reflexiveViewModel        = reflexiveViewModel with {
        Key                     = attribute.Key,
        Description             = attribute.Description,
        DisplayGroup            = attribute.DisplayGroup,
        DefaultValue            = attribute.DefaultValue,
        IsRequired              = attribute.IsRequired,
        SortOrder               = attribute.SortOrder,
        Title                   = attribute.Title
      };

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var viewModel = new AttributeViewModel<AttributeDescriptorViewModel>(currentTopic, reflexiveViewModel);

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(viewModel);

    }

  } // Class
Example #15
0
    /*==========================================================================================================================
    | 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
          }
        );
      }

    }
    /*==========================================================================================================================
    | METHOD: INVOKE
    \-------------------------------------------------------------------------------------------------------------------------*/
    /// <summary>
    ///   Assembles the view model for the <see cref="IncomingRelationshipViewComponent"/>.
    /// </summary>
    public IViewComponentResult Invoke(
      EditingTopicViewModel currentTopic,
      IncomingRelationshipAttributeDescriptorViewModel attribute,
      string htmlFieldPrefix
    ) {

      /*------------------------------------------------------------------------------------------------------------------------
      | Validate parameters
      \-----------------------------------------------------------------------------------------------------------------------*/
      Contract.Requires(currentTopic, nameof(currentTopic));
      Contract.Requires(attribute, nameof(attribute));
      Contract.Requires(attribute.Key, nameof(attribute.Key));

      /*------------------------------------------------------------------------------------------------------------------------
      | Set HTML prefix
      \-----------------------------------------------------------------------------------------------------------------------*/
      ViewData.TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix;

      /*------------------------------------------------------------------------------------------------------------------------
      | Establish view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      var model = new IncomingRelationshipAttributeViewModel(currentTopic, attribute);

      /*------------------------------------------------------------------------------------------------------------------------
      | Set incoming relationships
      >-------------------------------------------------------------------------------------------------------------------------
      | ### NOTE JJC20200929: This would be a lot cleaner using the ITopicMappingService. But that would introduce an additional
      | dependency on the StandardEditorComposer, which would be a breaking change. We can reevaluate this in the future if
      | other view components would benefit from this.
      \-----------------------------------------------------------------------------------------------------------------------*/
      var topic                 = _topicRepository.Load(currentTopic.UniqueKey);

      Contract.Assume(topic, $"The target topic with the unique key '{currentTopic.UniqueKey}' could not be found.");

      foreach(var relatedTopic in topic.IncomingRelationships.GetValues(attribute.RelationshipKey?? attribute.Key)) {

        if (!String.IsNullOrWhiteSpace(attribute.AttributeKey)) {
          var attributeValue = relatedTopic.Attributes.GetValue(attribute.AttributeKey, "");
          if (attribute.AttributeKey.Equals("ContentType", StringComparison.OrdinalIgnoreCase)) {
            attributeValue = relatedTopic.ContentType;
          }
          if (!attributeValue.Equals(attribute.AttributeValue, StringComparison.OrdinalIgnoreCase)) {
            continue;
          }
        }
        var relatedViewModel    = new CoreTopicViewModel {
          Key                   = relatedTopic.Key,
          ContentType           = relatedTopic.ContentType,
          UniqueKey             = relatedTopic.GetUniqueKey(),
          WebPath               = relatedTopic.GetWebPath(),
          Title                 = relatedTopic.Title
        };
        model.RelatedTopics.Add(relatedViewModel);
      }

      /*------------------------------------------------------------------------------------------------------------------------
      | Return view with view model
      \-----------------------------------------------------------------------------------------------------------------------*/
      return View(model);

    }