Files
Umbraco-CMS/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs

194 lines
8.4 KiB
C#
Raw Normal View History

2021-01-13 12:48:41 +11:00
using System;
2019-06-24 11:58:36 +02:00
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Implement;
using Umbraco.Cms.Core.Strings;
Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB10314-mb-embedded-dependencies # Conflicts: # src/Umbraco.Infrastructure/ModelsBuilder/ApiVersion.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TypeModel.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TypeModelHasher.cs # src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderDashboard.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsGenerationError.cs # src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs # src/Umbraco.Infrastructure/ModelsBuilder/PublishedElementExtensions.cs # src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs # src/Umbraco.Infrastructure/ModelsBuilder/RoslynCompiler.cs # src/Umbraco.Infrastructure/ModelsBuilder/TypeExtensions.cs # src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs # src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs # src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj # src/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/UmbracoApplicationTests.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/MediaTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/MemberTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs # src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderExtensions.cs # src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs # src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs # src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs # src/Umbraco.Web.Common/ModelsBuilder/UmbracoAssemblyLoadContext.cs # src/Umbraco.Web.UI.NetCore/Startup.cs # src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs
2021-02-22 09:00:33 +01:00
using Umbraco.Cms.Infrastructure.ModelsBuilder;
using Umbraco.Cms.Infrastructure.Services.Notifications;
using Umbraco.Cms.Infrastructure.WebAssets;
2021-02-10 11:42:04 +01:00
using Umbraco.Cms.Web.Common.ModelBinders;
2019-06-24 11:58:36 +02:00
Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB10314-mb-embedded-dependencies # Conflicts: # src/Umbraco.Infrastructure/ModelsBuilder/ApiVersion.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/ModelsGenerator.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TextHeaderWriter.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TypeModel.cs # src/Umbraco.Infrastructure/ModelsBuilder/Building/TypeModelHasher.cs # src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderDashboard.cs # src/Umbraco.Infrastructure/ModelsBuilder/ModelsGenerationError.cs # src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs # src/Umbraco.Infrastructure/ModelsBuilder/PublishedElementExtensions.cs # src/Umbraco.Infrastructure/ModelsBuilder/PublishedModelUtility.cs # src/Umbraco.Infrastructure/ModelsBuilder/RoslynCompiler.cs # src/Umbraco.Infrastructure/ModelsBuilder/TypeExtensions.cs # src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs # src/Umbraco.ModelsBuilder.Embedded/ImplementPropertyTypeAttribute.cs # src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj # src/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/BuilderTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.ModelsBuilder.Embedded/UmbracoApplicationTests.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ContentTypeModelValidatorBase.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/MediaTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/MemberTypeModelValidator.cs # src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs # src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderExtensions.cs # src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs # src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs # src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs # src/Umbraco.Web.Common/ModelsBuilder/UmbracoAssemblyLoadContext.cs # src/Umbraco.Web.UI.NetCore/Startup.cs # src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs
2021-02-22 09:00:33 +01:00
namespace Umbraco.Cms.Web.Common.ModelsBuilder
2019-06-24 11:58:36 +02:00
{
2021-01-13 12:48:41 +11:00
/// <summary>
/// Handles <see cref="UmbracoApplicationStarting"/> and <see cref="ServerVariablesParsing"/> notifications to initialize MB
/// </summary>
internal class ModelsBuilderNotificationHandler :
INotificationHandler<ServerVariablesParsing>,
INotificationHandler<ModelBindingError>,
INotificationHandler<TemplateSavingNotification>
2019-06-24 11:58:36 +02:00
{
private readonly ModelsBuilderSettings _config;
private readonly IShortStringHelper _shortStringHelper;
private readonly IModelsBuilderDashboardProvider _modelsBuilderDashboardProvider;
2019-06-24 11:58:36 +02:00
2021-01-13 12:48:41 +11:00
public ModelsBuilderNotificationHandler(
IOptions<ModelsBuilderSettings> config,
IShortStringHelper shortStringHelper,
IModelsBuilderDashboardProvider modelsBuilderDashboardProvider)
2019-06-24 11:58:36 +02:00
{
_config = config.Value;
_shortStringHelper = shortStringHelper;
_modelsBuilderDashboardProvider = modelsBuilderDashboardProvider;
2019-06-24 11:58:36 +02:00
}
2021-01-13 12:48:41 +11:00
/// <summary>
/// Handles the <see cref="ServerVariablesParsing"/> notification to add custom urls and MB mode
2021-01-13 12:48:41 +11:00
/// </summary>
2021-01-25 07:37:05 +01:00
public void Handle(ServerVariablesParsing notification)
2019-06-24 11:58:36 +02:00
{
IDictionary<string, object> serverVars = notification.ServerVariables;
if (!serverVars.ContainsKey("umbracoUrls"))
2021-01-13 12:48:41 +11:00
{
throw new ArgumentException("Missing umbracoUrls.");
2021-01-13 12:48:41 +11:00
}
var umbracoUrlsObject = serverVars["umbracoUrls"];
if (umbracoUrlsObject == null)
2021-01-13 12:48:41 +11:00
{
throw new ArgumentException("Null umbracoUrls");
2021-01-13 12:48:41 +11:00
}
if (!(umbracoUrlsObject is Dictionary<string, object> umbracoUrls))
2021-01-13 12:48:41 +11:00
{
throw new ArgumentException("Invalid umbracoUrls");
2021-01-13 12:48:41 +11:00
}
if (!serverVars.ContainsKey("umbracoPlugins"))
2021-01-13 12:48:41 +11:00
{
throw new ArgumentException("Missing umbracoPlugins.");
2021-01-13 12:48:41 +11:00
}
if (!(serverVars["umbracoPlugins"] is Dictionary<string, object> umbracoPlugins))
2021-01-13 12:48:41 +11:00
{
throw new ArgumentException("Invalid umbracoPlugins");
2021-01-13 12:48:41 +11:00
}
umbracoUrls["modelsBuilderBaseUrl"] = _modelsBuilderDashboardProvider.GetUrl();
2021-01-13 12:48:41 +11:00
umbracoPlugins["modelsBuilder"] = GetModelsBuilderSettings();
2019-06-24 11:58:36 +02:00
}
private Dictionary<string, object> GetModelsBuilderSettings()
{
var settings = new Dictionary<string, object>
{
{"mode", _config.ModelsMode.ToString() }
2019-06-24 11:58:36 +02:00
};
return settings;
}
/// <summary>
/// Used to check if a template is being created based on a document type, in this case we need to
/// ensure the template markup is correct based on the model name of the document type
/// </summary>
public void Handle(TemplateSavingNotification notification)
2019-06-24 11:58:36 +02:00
{
if (_config.ModelsMode == ModelsMode.Nothing)
{
return;
}
// Don't do anything if we're not requested to create a template for a content type
if (notification.CreateTemplateForContentType is null or false)
{
return;
}
2019-06-24 11:58:36 +02:00
// ensure we have the content type alias
if (notification.ContentTypeAlias is null)
2021-01-13 12:48:41 +11:00
{
throw new InvalidOperationException("ContentTypeAlias was not found on the notification");
2021-01-13 12:48:41 +11:00
}
2019-06-24 11:58:36 +02:00
foreach (ITemplate template in notification.SavedEntities)
2021-01-13 12:48:41 +11:00
{
2019-06-24 11:58:36 +02:00
// if it is in fact a new entity (not been saved yet) and the "CreateTemplateForContentType" key
// is found, then it means a new template is being created based on the creation of a document type
if (!template.HasIdentity && string.IsNullOrWhiteSpace(template.Content))
{
// ensure is safe and always pascal cased, per razor standard
// + this is how we get the default model name in Umbraco.ModelsBuilder.Umbraco.Application
var alias = notification.ContentTypeAlias;
2019-06-24 11:58:36 +02:00
var name = template.Name; // will be the name of the content type since we are creating
var className = UmbracoServices.GetClrName(_shortStringHelper, name, alias);
2019-06-24 11:58:36 +02:00
var modelNamespace = _config.ModelsNamespace;
// we do not support configuring this at the moment, so just let Umbraco use its default value
2021-01-13 12:48:41 +11:00
// var modelNamespaceAlias = ...;
2019-06-24 11:58:36 +02:00
var markup = ViewHelper.GetDefaultFileContent(
modelClassName: className,
modelNamespace: modelNamespace/*,
modelNamespaceAlias: modelNamespaceAlias*/);
2021-01-13 12:48:41 +11:00
// set the template content to the new markup
2019-06-24 11:58:36 +02:00
template.Content = markup;
}
2021-01-13 12:48:41 +11:00
}
2019-06-24 11:58:36 +02:00
}
2021-02-01 15:37:41 +11:00
/// <summary>
/// Handles when a model binding error occurs
/// </summary>
public void Handle(ModelBindingError notification)
2019-06-24 11:58:36 +02:00
{
2021-02-01 15:37:41 +11:00
ModelsBuilderAssemblyAttribute sourceAttr = notification.SourceType.Assembly.GetCustomAttribute<ModelsBuilderAssemblyAttribute>();
ModelsBuilderAssemblyAttribute modelAttr = notification.ModelType.Assembly.GetCustomAttribute<ModelsBuilderAssemblyAttribute>();
2019-06-24 11:58:36 +02:00
// if source or model is not a ModelsBuider type...
if (sourceAttr == null || modelAttr == null)
{
// if neither are ModelsBuilder types, give up entirely
if (sourceAttr == null && modelAttr == null)
2021-01-13 12:48:41 +11:00
{
2019-06-24 11:58:36 +02:00
return;
2021-01-13 12:48:41 +11:00
}
2019-06-24 11:58:36 +02:00
// else report, but better not restart (loops?)
2021-02-01 15:37:41 +11:00
notification.Message.Append(" The ");
notification.Message.Append(sourceAttr == null ? "view model" : "source");
notification.Message.Append(" is a ModelsBuilder type, but the ");
notification.Message.Append(sourceAttr != null ? "view model" : "source");
notification.Message.Append(" is not. The application is in an unstable state and should be restarted.");
2019-06-24 11:58:36 +02:00
return;
}
// both are ModelsBuilder types
var pureSource = sourceAttr.PureLive;
var pureModel = modelAttr.PureLive;
2019-06-24 11:58:36 +02:00
if (sourceAttr.PureLive || modelAttr.PureLive)
2021-01-13 12:48:41 +11:00
{
if (pureSource == false || pureModel == false)
{
2019-06-24 11:58:36 +02:00
// only one is pure - report, but better not restart (loops?)
2021-02-01 15:37:41 +11:00
notification.Message.Append(pureSource
? " The content model is PureLive, but the view model is not."
: " The view model is PureLive, but the content model is not.");
2021-02-01 15:37:41 +11:00
notification.Message.Append(" The application is in an unstable state and should be restarted.");
}
else
{
2019-06-24 11:58:36 +02:00
// both are pure - report, and if different versions, restart
// if same version... makes no sense... and better not restart (loops?)
Version sourceVersion = notification.SourceType.Assembly.GetName().Version;
Version modelVersion = notification.ModelType.Assembly.GetName().Version;
2021-02-01 15:37:41 +11:00
notification.Message.Append(" Both view and content models are PureLive, with ");
notification.Message.Append(sourceVersion == modelVersion
? "same version. The application is in an unstable state and should be restarted."
: "different versions. The application is in an unstable state and should be restarted.");
}
2021-01-13 12:48:41 +11:00
}
2019-06-24 11:58:36 +02:00
}
}
}