|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Globalization;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures;
internal sealed class TemplateBuilder
{
private readonly IViewEngine _viewEngine;
private readonly IViewBufferScope _bufferScope;
private readonly ViewContext _viewContext;
private readonly ViewDataDictionary _viewData;
private readonly ModelExplorer _modelExplorer;
private object _model;
private readonly ModelMetadata _metadata;
private readonly string _htmlFieldName;
private readonly string _templateName;
private readonly bool _readOnly;
private readonly object _additionalViewData;
public TemplateBuilder(
IViewEngine viewEngine,
IViewBufferScope bufferScope,
ViewContext viewContext,
ViewDataDictionary viewData,
ModelExplorer modelExplorer,
string htmlFieldName,
string templateName,
bool readOnly,
object additionalViewData)
{
ArgumentNullException.ThrowIfNull(viewEngine);
ArgumentNullException.ThrowIfNull(bufferScope);
ArgumentNullException.ThrowIfNull(viewContext);
ArgumentNullException.ThrowIfNull(viewData);
ArgumentNullException.ThrowIfNull(modelExplorer);
_viewEngine = viewEngine;
_bufferScope = bufferScope;
_viewContext = viewContext;
_viewData = viewData;
_modelExplorer = modelExplorer;
_htmlFieldName = htmlFieldName;
_templateName = templateName;
_readOnly = readOnly;
_additionalViewData = additionalViewData;
_model = modelExplorer.Model;
_metadata = modelExplorer.Metadata;
}
public IHtmlContent Build()
{
if (_metadata.ConvertEmptyStringToNull && string.Empty.Equals(_model))
{
_model = null;
}
// Normally this shouldn't happen, unless someone writes their own custom Object templates which
// don't check to make sure that the object hasn't already been displayed
if (_viewData.TemplateInfo.Visited(_modelExplorer))
{
return HtmlString.Empty;
}
// Create VDD of type object so any model type is allowed.
var viewData = new ViewDataDictionary<object>(_viewData);
// Create a new ModelExplorer in order to preserve the model metadata of the original _viewData even
// though _model may have been reset to null. Otherwise we might lose track of the model type /property.
viewData.ModelExplorer = _modelExplorer.GetExplorerForModel(_model);
var formatString = _readOnly ?
viewData.ModelMetadata.DisplayFormatString :
viewData.ModelMetadata.EditFormatString;
var formattedModelValue = _model;
if (_model == null)
{
if (_readOnly)
{
formattedModelValue = _metadata.NullDisplayText;
}
}
else if (!string.IsNullOrEmpty(formatString))
{
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, _model);
}
else if ((string.Equals("week", _templateName, StringComparison.OrdinalIgnoreCase) ||
string.Equals("week", viewData.ModelMetadata.DataTypeName, StringComparison.OrdinalIgnoreCase)))
{
// "week" is a new HTML5 input type that only will be rendered in Rfc3339 mode
formattedModelValue = FormatWeekHelper.GetFormattedWeek(_modelExplorer);
}
else if (viewData.ModelMetadata.IsEnum && _model is Enum modelEnum)
{
// Cover the case where the model is an enum and we want the string value of it
var value = modelEnum.ToString("d");
var enumGrouped = viewData.ModelMetadata.EnumGroupedDisplayNamesAndValues;
Debug.Assert(enumGrouped != null);
foreach (var kvp in enumGrouped)
{
if (kvp.Value == value)
{
// Creates a ModelExplorer with the same Metadata except that the Model is a string instead of an Enum
formattedModelValue = kvp.Key.Name;
break;
}
}
}
viewData.TemplateInfo.FormattedModelValue = formattedModelValue;
viewData.TemplateInfo.HtmlFieldPrefix = _viewData.TemplateInfo.GetFullHtmlFieldName(_htmlFieldName);
if (_additionalViewData != null)
{
foreach (var kvp in HtmlHelper.ObjectToDictionary(_additionalViewData))
{
viewData[kvp.Key] = kvp.Value;
}
}
var visitedObjectsKey = _model ?? _modelExplorer.ModelType;
viewData.TemplateInfo.AddVisited(visitedObjectsKey);
var templateRenderer = new TemplateRenderer(
_viewEngine,
_bufferScope,
_viewContext,
viewData,
_templateName,
_readOnly);
return templateRenderer.Render();
}
}
|