diff --git a/src/Umbraco.ModelsBuilder/Api/ApiHelper.cs b/src/Umbraco.ModelsBuilder/Api/ApiHelper.cs index 3d9f94bbd4..fa6492fe3f 100644 --- a/src/Umbraco.ModelsBuilder/Api/ApiHelper.cs +++ b/src/Umbraco.ModelsBuilder/Api/ApiHelper.cs @@ -5,13 +5,11 @@ using Umbraco.ModelsBuilder.Umbraco; namespace Umbraco.ModelsBuilder.Api { - // internal to be used by Umbraco.ModelsBuilder.Api project internal static class ApiHelper { - public static Dictionary GetModels(string modelsNamespace, IDictionary files) + public static Dictionary GetModels(UmbracoServices umbracoServices, string modelsNamespace, IDictionary files) { - var umbraco = ModelsBuilderComponent.Umbraco; - var typeModels = umbraco.GetAllTypes(); + var typeModels = umbracoServices.GetAllTypes(); var parseResult = new CodeParser().ParseWithReferencedAssemblies(files); var builder = new TextBuilder(typeModels, parseResult, modelsNamespace); diff --git a/src/Umbraco.ModelsBuilder/Api/ModelsBuilderApiController.cs b/src/Umbraco.ModelsBuilder/Api/ModelsBuilderApiController.cs index d74006c50f..444910b069 100644 --- a/src/Umbraco.ModelsBuilder/Api/ModelsBuilderApiController.cs +++ b/src/Umbraco.ModelsBuilder/Api/ModelsBuilderApiController.cs @@ -1,16 +1,12 @@ using System; -using System.Collections.Generic; using System.Net; using System.Net.Http; -using System.Text; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.ModelsBuilder.Building; using Umbraco.ModelsBuilder.Configuration; +using Umbraco.ModelsBuilder.Umbraco; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; -using Constants = Umbraco.Core.Constants; namespace Umbraco.ModelsBuilder.Api { @@ -28,6 +24,13 @@ namespace Umbraco.ModelsBuilder.Api { public const string ControllerArea = "ModelsBuilder"; + private readonly UmbracoServices _umbracoServices; + + public ModelsBuilderApiController(UmbracoServices umbracoServices) + { + _umbracoServices = umbracoServices; + } + // invoked by the API [System.Web.Http.HttpPost] // use the http one, not mvc, with api controllers! [ApiBasicAuthFilter("developer")] // have to use our own, non-cookie-based, auth @@ -60,7 +63,7 @@ namespace Umbraco.ModelsBuilder.Api if (!checkResult.Success) return checkResult.Result; - var models = ApiHelper.GetModels(data.Namespace, data.Files); + var models = ApiHelper.GetModels(_umbracoServices, data.Namespace, data.Files); return Request.CreateResponse(HttpStatusCode.OK, models, Configuration.Formatters.JsonFormatter); } diff --git a/src/Umbraco.ModelsBuilder/Properties/AssemblyInfo.cs b/src/Umbraco.ModelsBuilder/Properties/AssemblyInfo.cs index a0a395a8a8..a2f8d1ae1e 100644 --- a/src/Umbraco.ModelsBuilder/Properties/AssemblyInfo.cs +++ b/src/Umbraco.ModelsBuilder/Properties/AssemblyInfo.cs @@ -8,3 +8,7 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] [assembly: Guid("7020a059-c0d1-43a0-8efd-23591a0c9af6")] + +// code analysis +// IDE1006 is broken, wants _value syntax for consts, etc - and it's even confusing ppl at MS, kill it +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "~_~")] diff --git a/src/Umbraco.ModelsBuilder/Umbraco.ModelsBuilder.csproj b/src/Umbraco.ModelsBuilder/Umbraco.ModelsBuilder.csproj index 0279878b93..b9a5890d57 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco.ModelsBuilder.csproj +++ b/src/Umbraco.ModelsBuilder/Umbraco.ModelsBuilder.csproj @@ -87,11 +87,10 @@ - + - diff --git a/src/Umbraco.ModelsBuilder/Umbraco/LiveModelsProvider.cs b/src/Umbraco.ModelsBuilder/Umbraco/LiveModelsProvider.cs index 1172fee59c..b6c37a3558 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco/LiveModelsProvider.cs +++ b/src/Umbraco.ModelsBuilder/Umbraco/LiveModelsProvider.cs @@ -17,6 +17,7 @@ namespace Umbraco.ModelsBuilder.Umbraco // supports LiveDll and LiveAppData - but not PureLive public sealed class LiveModelsProvider { + private static UmbracoServices _umbracoServices; private static Mutex _mutex; private static int _req; @@ -30,12 +31,14 @@ namespace Umbraco.ModelsBuilder.Umbraco } } - internal static void Install() + internal static void Install(UmbracoServices umbracoServices) { // just be sure if (!IsEnabled) return; + _umbracoServices = umbracoServices; + // initialize mutex // ApplicationId will look like "/LM/W3SVC/1/Root/AppName" // name is system-wide and must be less than 260 chars @@ -110,11 +113,11 @@ namespace Umbraco.ModelsBuilder.Umbraco var config = UmbracoConfig.For.ModelsBuilder(); // EnableDllModels will recycle the app domain - but this request will end properly - ModelsBuilderBackOfficeController.GenerateModels(modelsDirectory, config.ModelsMode.IsAnyDll() ? bin : null); + ModelsBuilderBackOfficeController.GenerateModels(_umbracoServices, modelsDirectory, config.ModelsMode.IsAnyDll() ? bin : null); } } - // have to do this because it's the only way to subscribe to EndRequest + // have to do this because it's the only way to subscribe to EndRequest, // module is installed by assembly attribute at the top of this file public class LiveModelsProviderModule : IHttpModule { diff --git a/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderApplication.cs b/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderApplication.cs deleted file mode 100644 index bf650804c7..0000000000 --- a/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderApplication.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using LightInject; -using Umbraco.Core; -using Umbraco.Core.Components; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; -using Umbraco.ModelsBuilder.Configuration; -using Umbraco.Web; -using Umbraco.Web.UI.JavaScript; - -namespace Umbraco.ModelsBuilder.Umbraco -{ - /// - /// Installs ModelsBuilder into the Umbraco site. - /// - /// - /// Don't bother installing at all, if not RuntimeLevel.Run. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - public class ModelsBuilderApplication : UmbracoComponentBase, IUmbracoCoreComponent - { - private IRuntimeState _runtimeState; - - public override void Compose(Composition composition) - { - var config = UmbracoConfig.For.ModelsBuilder(); - - if (config.ModelsMode == ModelsMode.PureLive) - InstallLiveModels(composition.Container); - else if (config.EnableFactory) - InstallDefaultModelsFactory(composition.Container); - - // always setup the dashboard - InstallServerVars(); - } - - public void Initialize(IRuntimeState runtimeState) - { - _runtimeState = runtimeState; - - var config = UmbracoConfig.For.ModelsBuilder(); - - if (config.Enable) - FileService.SavingTemplate += FileService_SavingTemplate; - - if (config.ModelsMode.IsLiveNotPure()) - LiveModelsProvider.Install(); - - if (config.FlagOutOfDateModels) - OutOfDateModelsStatus.Install(); - } - - private void InstallDefaultModelsFactory(IServiceContainer container) - { - var types = Current.TypeLoader.GetTypes(); - var factory = new PublishedModelFactory(types); - container.RegisterSingleton(_ => factory); - } - - private void InstallLiveModels(IServiceContainer container) - { - container.RegisterSingleton(); - - // the following would add @using statement in every view so user's don't - // have to do it - however, then noone understands where the @using statement - // comes from, and it cannot be avoided / removed --- DISABLED - // - /* - // no need for @using in views - // note: - // we are NOT using the in-code attribute here, config is required - // because that would require parsing the code... and what if it changes? - // we can AddGlobalImport not sure we can remove one anyways - var modelsNamespace = Configuration.Config.ModelsNamespace; - if (string.IsNullOrWhiteSpace(modelsNamespace)) - modelsNamespace = Configuration.Config.DefaultModelsNamespace; - System.Web.WebPages.Razor.WebPageRazorHost.AddGlobalImport(modelsNamespace); - */ - } - - private void InstallServerVars() - { - // register our url - for the backoffice api - ServerVariablesParser.Parsing += (sender, serverVars) => - { - if (!serverVars.ContainsKey("umbracoUrls")) - throw new Exception("Missing umbracoUrls."); - var umbracoUrlsObject = serverVars["umbracoUrls"]; - if (umbracoUrlsObject == null) - throw new Exception("Null umbracoUrls"); - if (!(umbracoUrlsObject is Dictionary umbracoUrls)) - throw new Exception("Invalid umbracoUrls"); - - if (!serverVars.ContainsKey("umbracoPlugins")) - throw new Exception("Missing umbracoPlugins."); - if (!(serverVars["umbracoPlugins"] is Dictionary umbracoPlugins)) - throw new Exception("Invalid umbracoPlugins"); - - if (HttpContext.Current == null) throw new InvalidOperationException("HttpContext is null"); - var urlHelper = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData())); - - umbracoUrls["modelsBuilderBaseUrl"] = urlHelper.GetUmbracoApiServiceBaseUrl(controller => controller.BuildModels()); - umbracoPlugins["modelsBuilder"] = GetModelsBuilderSettings(); - }; - } - - private Dictionary GetModelsBuilderSettings() - { - if (_runtimeState.Level != RuntimeLevel.Run) - return null; - - var settings = new Dictionary - { - {"enabled", UmbracoConfig.For.ModelsBuilder().Enable} - }; - - return settings; - } - - /// - /// 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 - /// - /// - /// - private void FileService_SavingTemplate(IFileService sender, Core.Events.SaveEventArgs e) - { - // don't do anything if the factory is not enabled - // because, no factory = no models (even if generation is enabled) - if (!UmbracoConfig.For.ModelsBuilder().EnableFactory) return; - - // don't do anything if this special key is not found - if (!e.AdditionalData.ContainsKey("CreateTemplateForContentType")) return; - - // ensure we have the content type alias - if (!e.AdditionalData.ContainsKey("ContentTypeAlias")) - throw new InvalidOperationException("The additionalData key: ContentTypeAlias was not found"); - - foreach (var template in e.SavedEntities) - { - // 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 && template.Content.IsNullOrWhiteSpace()) - { - // 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 = e.AdditionalData["ContentTypeAlias"].ToString(); - var name = template.Name; // will be the name of the content type since we are creating - var className = Application.GetClrName(name, alias); - - var modelNamespace = UmbracoConfig.For.ModelsBuilder().ModelsNamespace; - - // we do not support configuring this at the moment, so just let Umbraco use its default value - //var modelNamespaceAlias = ...; - - var markup = ViewHelper.GetDefaultFileContent( - modelClassName: className, - modelNamespace: modelNamespace/*, - modelNamespaceAlias: modelNamespaceAlias*/); - - //set the template content to the new markup - template.Content = markup; - } - } - } - } -} diff --git a/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderBackOfficeController.cs b/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderBackOfficeController.cs index 0a586fa16b..19c9bda5da 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderBackOfficeController.cs +++ b/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderBackOfficeController.cs @@ -11,6 +11,7 @@ using Umbraco.ModelsBuilder.Building; using Umbraco.ModelsBuilder.Configuration; using Umbraco.ModelsBuilder.Dashboard; using Umbraco.Web.Editors; +using Umbraco.Web.WebApi.Filters; namespace Umbraco.ModelsBuilder.Umbraco { @@ -22,8 +23,16 @@ namespace Umbraco.ModelsBuilder.Umbraco /// correct CSRF security is adhered to for angular and it also ensures that this controller is not subseptipal to /// global WebApi formatters being changed since this is always forced to only return Angular JSON Specific formats. /// + [UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)] public class ModelsBuilderBackOfficeController : UmbracoAuthorizedJsonController { + private readonly UmbracoServices _umbracoServices; + + public ModelsBuilderBackOfficeController(UmbracoServices umbracoServices) + { + _umbracoServices = umbracoServices; + } + // invoked by the dashboard // requires that the user is logged into the backoffice and has access to the developer section // beware! the name of the method appears in modelsbuilder.controller.js @@ -93,7 +102,12 @@ namespace Umbraco.ModelsBuilder.Umbraco }; } - internal static void GenerateModels(string modelsDirectory, string bin) + private void GenerateModels(string modelsDirectory, string bin) + { + GenerateModels(_umbracoServices, modelsDirectory, bin); + } + + internal static void GenerateModels(UmbracoServices umbracoServices, string modelsDirectory, string bin) { if (!Directory.Exists(modelsDirectory)) Directory.CreateDirectory(modelsDirectory); @@ -101,8 +115,7 @@ namespace Umbraco.ModelsBuilder.Umbraco foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) File.Delete(file); - var umbraco = ModelsBuilderComponent.Umbraco; - var typeModels = umbraco.GetAllTypes(); + var typeModels = umbracoServices.GetAllTypes(); var ourFiles = Directory.GetFiles(modelsDirectory, "*.cs").ToDictionary(x => x, File.ReadAllText); var parseResult = new CodeParser().ParseWithReferencedAssemblies(ourFiles); @@ -178,4 +191,4 @@ namespace Umbraco.ModelsBuilder.Umbraco public OutOfDateType Status { get; set; } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderComponent.cs b/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderComponent.cs index ba75b59578..a581319ba5 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderComponent.cs +++ b/src/Umbraco.ModelsBuilder/Umbraco/ModelsBuilderComponent.cs @@ -12,6 +12,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; +using Umbraco.ModelsBuilder.Api; using Umbraco.ModelsBuilder.Configuration; using Umbraco.Web; using Umbraco.Web.PublishedCache.NuCache; @@ -19,59 +20,55 @@ using Umbraco.Web.UI.JavaScript; namespace Umbraco.ModelsBuilder.Umbraco { - // fixme - // nucache components wants models so we need to setup models before - // however for some reason, this creates a cyclic dependency? => need better debugging info - // cos nucache is Core so we need to be Core too - // also... should have a generic "modelsbuilder" and "contentcache" components for dependencies! - [RequiredComponent(typeof(NuCacheComponent))] + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] public class ModelsBuilderComponent : UmbracoComponentBase, IUmbracoCoreComponent { public override void Compose(Composition composition) { base.Compose(composition); - composition.Container.Register(new PerContainerLifetime()); + + composition.Container.Register(new PerContainerLifetime()); var config = UmbracoConfig.For.ModelsBuilder(); if (config.ModelsMode == ModelsMode.PureLive) - InstallLiveModels(composition.Container); + ComposeForLiveModels(composition.Container); else if (config.EnableFactory) - InstallDefaultModelsFactory(composition.Container); + ComposeForDefaultModelsFactory(composition.Container); // always setup the dashboard InstallServerVars(composition.Container.GetInstance().Level); + composition.Container.Register(typeof(ModelsBuilderBackOfficeController), new PerRequestLifeTime()); - // need to do it here 'cos NuCache wants it during compose? - Umbraco = composition.Container.GetInstance(); + // setup the API if enabled (and in debug mode) + if (config.ApiServer) + composition.Container.Register(typeof(ModelsBuilderApiController), new PerRequestLifeTime()); } - public void Initialize(Application application) + public void Initialize(UmbracoServices umbracoServices) { - Umbraco = application; - var config = UmbracoConfig.For.ModelsBuilder(); if (config.Enable) FileService.SavingTemplate += FileService_SavingTemplate; + // fixme LiveModelsProvider should not be static if (config.ModelsMode.IsLiveNotPure()) - LiveModelsProvider.Install(); + LiveModelsProvider.Install(umbracoServices); + // fixme OutOfDateModelsStatus should not be static if (config.FlagOutOfDateModels) OutOfDateModelsStatus.Install(); } - public static Application Umbraco { get; private set; } - - private void InstallDefaultModelsFactory(IServiceContainer container) + private void ComposeForDefaultModelsFactory(IServiceContainer container) { container.RegisterSingleton(factory => new PublishedModelFactory(factory.GetInstance().GetTypes())); } - private void InstallLiveModels(IServiceContainer container) + private void ComposeForLiveModels(IServiceContainer container) { container.RegisterSingleton(); @@ -102,14 +99,12 @@ namespace Umbraco.ModelsBuilder.Umbraco var umbracoUrlsObject = serverVars["umbracoUrls"]; if (umbracoUrlsObject == null) throw new Exception("Null umbracoUrls"); - var umbracoUrls = umbracoUrlsObject as Dictionary; - if (umbracoUrls == null) + if (!(umbracoUrlsObject is Dictionary umbracoUrls)) throw new Exception("Invalid umbracoUrls"); if (!serverVars.ContainsKey("umbracoPlugins")) throw new Exception("Missing umbracoPlugins."); - var umbracoPlugins = serverVars["umbracoPlugins"] as Dictionary; - if (umbracoPlugins == null) + if (!(serverVars["umbracoPlugins"] is Dictionary umbracoPlugins)) throw new Exception("Invalid umbracoPlugins"); if (HttpContext.Current == null) throw new InvalidOperationException("HttpContext is null"); @@ -162,7 +157,7 @@ namespace Umbraco.ModelsBuilder.Umbraco // + this is how we get the default model name in Umbraco.ModelsBuilder.Umbraco.Application var alias = e.AdditionalData["ContentTypeAlias"].ToString(); var name = template.Name; // will be the name of the content type since we are creating - var className = Application.GetClrName(name, alias); + var className = UmbracoServices.GetClrName(name, alias); var modelNamespace = UmbracoConfig.For.ModelsBuilder().ModelsNamespace; @@ -180,4 +175,4 @@ namespace Umbraco.ModelsBuilder.Umbraco } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.ModelsBuilder/Umbraco/OutOfDateModelsStatus.cs b/src/Umbraco.ModelsBuilder/Umbraco/OutOfDateModelsStatus.cs index a5f21f2b2a..a047f21edb 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco/OutOfDateModelsStatus.cs +++ b/src/Umbraco.ModelsBuilder/Umbraco/OutOfDateModelsStatus.cs @@ -43,10 +43,7 @@ namespace Umbraco.ModelsBuilder.Umbraco File.Delete(path); } - public static bool IsEnabled - { - get { return UmbracoConfig.For.ModelsBuilder().FlagOutOfDateModels; } - } + public static bool IsEnabled => UmbracoConfig.For.ModelsBuilder().FlagOutOfDateModels; public static bool IsOutOfDate { diff --git a/src/Umbraco.ModelsBuilder/Umbraco/PureLiveModelFactory.cs b/src/Umbraco.ModelsBuilder/Umbraco/PureLiveModelFactory.cs index c216c92b03..9558c0140e 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco/PureLiveModelFactory.cs +++ b/src/Umbraco.ModelsBuilder/Umbraco/PureLiveModelFactory.cs @@ -36,13 +36,16 @@ namespace Umbraco.ModelsBuilder.Umbraco private int _ver, _skipver; private readonly int _debugLevel; private BuildManager _theBuildManager; + private readonly Lazy _umbracoServices; + private UmbracoServices UmbracoServices => _umbracoServices.Value; private static readonly Regex AssemblyVersionRegex = new Regex("AssemblyVersion\\(\"[0-9]+.[0-9]+.[0-9]+.[0-9]+\"\\)", RegexOptions.Compiled); private const string ProjVirt = "~/App_Data/Models/all.generated.cs"; private static readonly string[] OurFiles = { "models.hash", "models.generated.cs", "all.generated.cs", "all.dll.path", "models.err" }; - public PureLiveModelFactory(ProfilingLogger logger) + public PureLiveModelFactory(Lazy umbracoServices, ProfilingLogger logger) { + _umbracoServices = umbracoServices; _logger = logger; _ver = 1; // zero is for when we had no version _skipver = -1; // nothing to skip @@ -161,8 +164,7 @@ namespace Umbraco.ModelsBuilder.Umbraco if (_debugLevel > 0) _logger.Logger.Debug("RazorBuildProvider.CodeGenerationStarted"); - var provider = sender as RazorBuildProvider; - if (provider == null) return; + if (!(sender is RazorBuildProvider provider)) return; // add the assembly, and add a dependency to a text file that will change on each // compilation as in some environments (could not figure which/why) the BuildManager @@ -309,8 +311,7 @@ namespace Umbraco.ModelsBuilder.Umbraco .ToDictionary(x => x, File.ReadAllText) : new Dictionary(); - var umbraco = ModelsBuilderComponent.Umbraco; - var typeModels = umbraco.GetAllTypes(); + var typeModels = UmbracoServices.GetAllTypes(); var currentHash = HashHelper.Hash(ourFiles, typeModels); var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); var modelsSrcFile = Path.Combine(modelsDirectory, "models.generated.cs"); @@ -598,4 +599,4 @@ namespace Umbraco.ModelsBuilder.Umbraco #endregion } -} \ No newline at end of file +} diff --git a/src/Umbraco.ModelsBuilder/Umbraco/Application.cs b/src/Umbraco.ModelsBuilder/Umbraco/UmbracoServices.cs similarity index 97% rename from src/Umbraco.ModelsBuilder/Umbraco/Application.cs rename to src/Umbraco.ModelsBuilder/Umbraco/UmbracoServices.cs index df4549cc5c..f0347d9194 100644 --- a/src/Umbraco.ModelsBuilder/Umbraco/Application.cs +++ b/src/Umbraco.ModelsBuilder/Umbraco/UmbracoServices.cs @@ -12,14 +12,14 @@ using Umbraco.ModelsBuilder.Configuration; namespace Umbraco.ModelsBuilder.Umbraco { - public class Application + public class UmbracoServices { private readonly IContentTypeService _contentTypeService; private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; - public Application(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory) + public UmbracoServices(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory) { _contentTypeService = contentTypeService; _mediaTypeService = mediaTypeService;