diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index ae8ca25f52..6289a9bfc4 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -305,7 +305,7 @@ stages: - powershell: sqllocaldb start mssqllocaldb displayName: Start localdb (Windows only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - - powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest + - powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest displayName: Start SQL Server (Linux only) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - task: DotNetCoreCLI@2 @@ -494,7 +494,6 @@ stages: displayName: NuGet release dependsOn: - Deploy_MyGet - - Build_Docs condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.nuGetDeploy}})) jobs: - job: @@ -523,6 +522,7 @@ stages: dependsOn: - Build - Deploy_NuGet + - Build_Docs condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.uploadApiDocs}})) jobs: - job: diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index ec0538022f..86890d7fe8 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -1630,6 +1630,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Editor + Production.]]> Failed to delete template with ID %0% Edit template Sections diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 3e2f7c23dc..9641fda47f 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -1683,6 +1683,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Rich Text Editor + Production.]]> Failed to delete template with ID %0% Edit template Sections diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml b/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml index a9513c5302..1d76ac06f9 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/nl.xml @@ -1444,6 +1444,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Rich Text Editor + Production.]]> Kan sjabloon met ID %0% niet verwijderen Sjabloon aanpassen Secties diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs index bc83695d94..609c5305dc 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Mappers; +using Umbraco.Cms.Infrastructure.Runtime; namespace Umbraco.Extensions; @@ -17,6 +18,10 @@ public static partial class UmbracoBuilderExtensions public static MapperCollectionBuilder? Mappers(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); + /// + /// Gets the NPoco mappers collection builder. + /// + /// The builder. public static NPocoMapperCollectionBuilder? NPocoMappers(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); @@ -26,4 +31,11 @@ public static partial class UmbracoBuilderExtensions /// The builder. public static PackageMigrationPlanCollectionBuilder? PackageMigrationPlans(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); + + /// + /// Gets the runtime mode validators collection builder. + /// + /// The builder. + public static RuntimeModeValidatorCollectionBuilder RuntimeModeValidators(this IUmbracoBuilder builder) + => builder.WithCollectionBuilder(); } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 154bae9cd0..62bafcd28e 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -86,11 +86,12 @@ public static partial class UmbracoBuilderExtensions // Add runtime mode validation builder.Services.AddSingleton(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); + builder.RuntimeModeValidators() + .Add() + .Add() + .Add() + .Add() + .Add(); // composers builder diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs index 04c60261ea..9f921266ca 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CreatedPackageSchemaRepository.cs @@ -652,10 +652,13 @@ public class CreatedPackageSchemaRepository : ICreatedPackagesRepository // the media file path is different from the URL and is specifically // extracted using the property editor for this media file and the current media file system. Stream mediaStream = _mediaFileManager.GetFile(media, out var mediaFilePath); - xmlMedia.Add(new XAttribute("mediaFilePath", mediaFilePath!)); + if (mediaFilePath is not null) + { + xmlMedia.Add(new XAttribute("mediaFilePath", mediaFilePath)); - // add the stream to our outgoing stream - mediaStreams.Add(mediaFilePath!, mediaStream); + // add the stream to our outgoing stream + mediaStreams.Add(mediaFilePath, mediaStream); + } } IEnumerable medias = _mediaService.GetByIds(definition.MediaUdis); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index 1a0a90269c..9e01320fdc 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -1,8 +1,10 @@ using System.Text; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -24,17 +26,26 @@ internal class TemplateRepository : EntityRepositoryBase, ITempl { private readonly IIOHelper _ioHelper; private readonly IShortStringHelper _shortStringHelper; - private readonly IViewHelper _viewHelper; private readonly IFileSystem? _viewsFileSystem; + private readonly IViewHelper _viewHelper; + private readonly IOptionsMonitor _runtimeSettings; - public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, - FileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper, IViewHelper viewHelper) + public TemplateRepository( + IScopeAccessor scopeAccessor, + AppCaches cache, + ILogger logger, + FileSystems fileSystems, + IIOHelper ioHelper, + IShortStringHelper shortStringHelper, + IViewHelper viewHelper, + IOptionsMonitor runtimeSettings) : base(scopeAccessor, cache, logger) { _ioHelper = ioHelper; _shortStringHelper = shortStringHelper; _viewsFileSystem = fileSystems.MvcViewsFileSystem; _viewHelper = viewHelper; + _runtimeSettings = runtimeSettings; } public Stream GetFileContentStream(string filepath) @@ -421,8 +432,12 @@ internal class TemplateRepository : EntityRepositoryBase, ITempl template.Id = nodeDto.NodeId; //Set Id on entity to ensure an Id is set template.Path = nodeDto.Path; - //now do the file work - SaveFile(template); + // Only save file when not in production runtime mode + if (_runtimeSettings.CurrentValue.Mode != RuntimeMode.Production) + { + //now do the file work + SaveFile(template); + } template.ResetDirtyProperties(); @@ -476,8 +491,12 @@ internal class TemplateRepository : EntityRepositoryBase, ITempl IEnumerable axisDefs = GetAxisDefinitions(dto); template.IsMasterTemplate = axisDefs.Any(x => x.ParentId == dto.NodeId); - //now do the file work - SaveFile((Template)entity, originalAlias); + // Only save file when not in production runtime mode + if (_runtimeSettings.CurrentValue.Mode != RuntimeMode.Production) + { + //now do the file work + SaveFile((Template)entity, originalAlias); + } entity.ResetDirtyProperties(); diff --git a/src/Umbraco.Infrastructure/PublishedContentQuery.cs b/src/Umbraco.Infrastructure/PublishedContentQuery.cs index 891f938a2b..ef16aeccc6 100644 --- a/src/Umbraco.Infrastructure/PublishedContentQuery.cs +++ b/src/Umbraco.Infrastructure/PublishedContentQuery.cs @@ -18,11 +18,11 @@ namespace Umbraco.Cms.Infrastructure; /// public class PublishedContentQuery : IPublishedContentQuery { - private static readonly HashSet _itemIdFieldNameHashSet = new() {ExamineFieldNames.ItemIdFieldName}; - private readonly IExamineManager _examineManager; private readonly IPublishedSnapshot _publishedSnapshot; private readonly IVariationContextAccessor _variationContextAccessor; + private static readonly HashSet _returnedQueryFields = + new() { ExamineFieldNames.ItemIdFieldName, ExamineFieldNames.CategoryFieldName }; /// /// Initializes a new instance of the class. @@ -293,8 +293,8 @@ public class PublishedContentQuery : IPublishedContentQuery ordering = query.ManagedQuery(term, fields); } - // Only select item ID field, because results are loaded from the published snapshot based on this single value - IOrdering? queryExecutor = ordering.SelectFields(_itemIdFieldNameHashSet); + // Filter selected fields because results are loaded from the published snapshot based on these + IOrdering? queryExecutor = ordering.SelectFields(_returnedQueryFields); ISearchResults? results = skip == 0 && take == 0 @@ -328,8 +328,8 @@ public class PublishedContentQuery : IPublishedContentQuery if (query is IOrdering ordering) { - // Only select item ID field, because results are loaded from the published snapshot based on this single value - query = ordering.SelectFields(_itemIdFieldNameHashSet); + // Filter selected fields because results are loaded from the published snapshot based on these + query = ordering.SelectFields(_returnedQueryFields); } ISearchResults? results = skip == 0 && take == 0 diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidationService.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidationService.cs index 85eec91786..c4bbeb6902 100644 --- a/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidationService.cs +++ b/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidationService.cs @@ -29,11 +29,18 @@ internal class RuntimeModeValidationService : IRuntimeModeValidationService var validationMessages = new List(); // Runtime mode validators are registered transient, but this service is registered as singleton - foreach (var runtimeModeValidator in _serviceProvider.GetServices()) + using (var scope = _serviceProvider.CreateScope()) { - if (runtimeModeValidator.Validate(runtimeMode, out var validationMessage) == false) + var runtimeModeValidators = scope.ServiceProvider.GetService(); + if (runtimeModeValidators is not null) { - validationMessages.Add(validationMessage); + foreach (var runtimeModeValidator in runtimeModeValidators) + { + if (runtimeModeValidator.Validate(runtimeMode, out var validationMessage) == false) + { + validationMessages.Add(validationMessage); + } + } } } diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidatorCollection.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidatorCollection.cs new file mode 100644 index 0000000000..97f25f3b8f --- /dev/null +++ b/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidatorCollection.cs @@ -0,0 +1,10 @@ +using Umbraco.Cms.Core.Composing; + +namespace Umbraco.Cms.Infrastructure.Runtime; + +public class RuntimeModeValidatorCollection : BuilderCollectionBase +{ + public RuntimeModeValidatorCollection(Func> items) + : base(items) + { } +} diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidatorCollectionBuilder.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidatorCollectionBuilder.cs new file mode 100644 index 0000000000..4c755a6040 --- /dev/null +++ b/src/Umbraco.Infrastructure/Runtime/RuntimeModeValidatorCollectionBuilder.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Composing; + +namespace Umbraco.Cms.Infrastructure.Runtime; + +public class RuntimeModeValidatorCollectionBuilder : SetCollectionBuilderBase +{ + protected override ServiceLifetime CollectionLifetime => ServiceLifetime.Transient; + + protected override RuntimeModeValidatorCollectionBuilder This => this; +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index b7fc6a92f5..0681ed957a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -597,7 +597,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { "assemblyVersion", _umbracoVersion.AssemblyVersion?.ToString() } }; - + app.Add("runtimeMode", _runtimeSettings.Mode.ToString()); //the value is the hash of the version, cdf version and the configured state app.Add("cacheBuster", $"{version}.{_runtimeState.Level}.{_runtimeMinifier.CacheBuster}".GenerateHash()); diff --git a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs index f4cefcc76b..fe4e3b2378 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs @@ -20,6 +20,8 @@ using Umbraco.Extensions; using Umbraco.Cms.Infrastructure.Packaging; using System.Xml.Linq; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Web.BackOffice.Controllers; @@ -47,6 +49,7 @@ public class DictionaryController : BackOfficeNotificationsController private readonly IHostingEnvironment _hostingEnvironment; private readonly PackageDataInstallation _packageDataInstallation; + [ActivatorUtilitiesConstructor] public DictionaryController( ILogger logger, ILocalizationService localizationService, @@ -60,8 +63,7 @@ public class DictionaryController : BackOfficeNotificationsController { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? - throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); + _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); _globalSettings = globalSettings.Value ?? throw new ArgumentNullException(nameof(globalSettings)); _localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); @@ -70,6 +72,27 @@ public class DictionaryController : BackOfficeNotificationsController _packageDataInstallation = packageDataInstallation ?? throw new ArgumentNullException(nameof(packageDataInstallation)); } + [Obsolete("Please use ctor that also takes an IEntityXmlSerializer, IHostingEnvironment & PackageDataInstallation instead, scheduled for removal in v12")] + public DictionaryController( + ILogger logger, + ILocalizationService localizationService, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IOptionsSnapshot globalSettings, + ILocalizedTextService localizedTextService, + IUmbracoMapper umbracoMapper) + : this( + logger, + localizationService, + backofficeSecurityAccessor, + globalSettings, + localizedTextService, + umbracoMapper, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) + { + } + /// /// Deletes a data type with a given ID /// diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs index 5b01d348d7..27c26004b9 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs @@ -180,10 +180,10 @@ public class ContentTypeTreeController : TreeController, ISearchableTree menu.Items.Add(LocalizedTextService, opensDialog: true, useLegacyIcon: false); - //no move action if this is a child doc type + // No move action if this is a child doc type if (parent == null) { - menu.Items.Add(LocalizedTextService, hasSeparator: true, opensDialog: true, useLegacyIcon: false); + menu.Items.Add(LocalizedTextService, hasSeparator: true, opensDialog: true, useLegacyIcon: false); } menu.Items.Add(LocalizedTextService, opensDialog: true, useLegacyIcon: false); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.controller.js index 2f7d879607..d72cdbe69a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.controller.js @@ -1,362 +1,367 @@ (function () { - "use strict"; + "use strict"; - function partialViewMacrosEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper, macroResource, editorService) { + function partialViewMacrosEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper, macroResource, editorService) { - var vm = this; + var vm = this; - vm.header = {}; - vm.header.editorfor = "visuallyHiddenTexts_newPartialViewMacro"; - vm.header.setPageTitle = true; - vm.page = {}; - vm.page.loading = true; - vm.partialViewMacroFile = {}; + vm.runtimeModeProduction = Umbraco.Sys.ServerVariables.application.runtimeMode == 'Production'; - //menu - vm.page.menu = {}; - vm.page.menu.currentSection = appState.getSectionState("currentSection"); - vm.page.menu.currentNode = null; + vm.header = {}; + vm.header.editorfor = "visuallyHiddenTexts_newPartialViewMacro"; + vm.header.setPageTitle = true; + vm.page = {}; + vm.page.loading = true; + vm.partialViewMacroFile = {}; - // insert buttons - vm.page.insertDefaultButton = { - labelKey: "general_insert", - addEllipsis: "true", - handler: function() { - vm.openInsertOverlay(); - } - }; - vm.page.insertSubButtons = [ - { - labelKey: "template_insertPageField", - addEllipsis: "true", - handler: function () { - vm.openPageFieldOverlay(); - } - }, - { - labelKey: "template_insertMacro", - addEllipsis: "true", - handler: function () { - vm.openMacroOverlay() - } - }, - { - labelKey: "template_insertDictionaryItem", - addEllipsis: "true", - handler: function () { - vm.openDictionaryItemOverlay(); - } - } - ]; + //menu + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; - // bind functions to view model - vm.save = save; - vm.openPageFieldOverlay = openPageFieldOverlay; - vm.openDictionaryItemOverlay = openDictionaryItemOverlay; - vm.openQueryBuilderOverlay = openQueryBuilderOverlay; - vm.openMacroOverlay = openMacroOverlay; - vm.openInsertOverlay = openInsertOverlay; + // insert buttons + vm.page.insertDefaultButton = { + labelKey: "general_insert", + addEllipsis: "true", + handler: function () { + vm.openInsertOverlay(); + } + }; + vm.page.insertSubButtons = [ + { + labelKey: "template_insertPageField", + addEllipsis: "true", + handler: function () { + vm.openPageFieldOverlay(); + } + }, + { + labelKey: "template_insertMacro", + addEllipsis: "true", + handler: function () { + vm.openMacroOverlay() + } + }, + { + labelKey: "template_insertDictionaryItem", + addEllipsis: "true", + handler: function () { + vm.openDictionaryItemOverlay(); + } + } + ]; - /* Functions bound to view model */ + // bind functions to view model + vm.save = save; + vm.openPageFieldOverlay = openPageFieldOverlay; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + vm.openMacroOverlay = openMacroOverlay; + vm.openInsertOverlay = openInsertOverlay; - function save() { + /* Functions bound to view model */ - vm.page.saveButtonState = "busy"; - vm.partialViewMacro.content = vm.editor.getValue(); + function save() { - contentEditingHelper.contentEditorPerformSave({ - saveMethod: codefileResource.save, - scope: $scope, - content: vm.partialViewMacro, - rebindCallback: function (orignal, saved) {} - }).then(function (saved) { - // create macro if needed - if($routeParams.create && $routeParams.nomacro !== "true") { - macroResource.createPartialViewMacroWithFile(saved.virtualPath, saved.name).then(function (created) { - navigationService.syncTree({ - tree: "macros", - path: '-1,new', - forceReload: true, - activate: false - }); - completeSave(saved); - }, Utilities.noop); - - - } else { - completeSave(saved); - } - - }, function (err) { - - vm.page.saveButtonState = "error"; - - localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { - notificationsService.error(headerValue, msgValue); - }); - }); + vm.page.saveButtonState = "busy"; + vm.partialViewMacro.content = vm.editor.getValue(); + contentEditingHelper.contentEditorPerformSave({ + saveMethod: codefileResource.save, + scope: $scope, + content: vm.partialViewMacro, + rebindCallback: function (orignal, saved) { } + }).then(function (saved) { + // create macro if needed + if ($routeParams.create && $routeParams.nomacro !== "true") { + macroResource.createPartialViewMacroWithFile(saved.virtualPath, saved.name).then(function (created) { + navigationService.syncTree({ + tree: "macros", + path: '-1,new', + forceReload: true, + activate: false }); + completeSave(saved); + }, Utilities.noop); + + } else { + completeSave(saved); } - function completeSave(saved) { + }, function (err) { - localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_partialViewSavedText").then(function (msgValue) { - notificationsService.success(headerValue, msgValue); - }); - }); + vm.page.saveButtonState = "error"; - //check if the name changed, if so we need to redirect - if (vm.partialViewMacro.id !== saved.id) { - contentEditingHelper.redirectToRenamedContent(saved.id); - } - else { - vm.page.saveButtonState = "success"; - vm.partialViewMacro = saved; + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); - //sync state - editorState.set(vm.partialViewMacro); - - // normal tree sync - navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - - // clear $dirty state on form - setFormState("pristine"); - } - - } - - function openInsertOverlay() { - var insertOverlay = { - allowedTypes: { - macro: true, - dictionary: true, - umbracoField: true - }, - submit: function(model) { - switch(model.insert.type) { - case "macro": - var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); - insert(macroObject.syntax); - break; - case "dictionary": - var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); - insert(code); - break; - case "umbracoField": - insert(model.insert.umbracoField); - break; - } - editorService.close(); - }, - close: function(oldModel) { - // close the dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.insertCodeSnippet(insertOverlay); - } - - function openMacroOverlay() { - var macroPicker = { - dialogData: {}, - submit: function (model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); - insert(macroObject.syntax); - editorService.close(); - }, - close: function() { - editorService.close(); - vm.editor.focus(); - } - }; - editorService.macroPicker(macroPicker); - } - - - function openPageFieldOverlay() { - var insertFieldEditor = { - submit: function (model) { - insert(model.umbracoField); - editorService.close(); - }, - close: function () { - editorService.close(); - vm.editor.focus(); - } - }; - editorService.insertField(insertFieldEditor); - } - - - function openDictionaryItemOverlay() { - - var labelKeys = [ - "template_insertDictionaryItem", - "emptyStates_emptyDictionaryTree" - ]; - - localizationService.localizeMany(labelKeys).then(function(values){ - var title = values[0]; - var emptyStateMessage = values[1]; - - var dictionaryPicker = { - section: "translation", - treeAlias: "dictionary", - entityType: "dictionary", - multiPicker: false, - title: title, - emptyStateMessage: emptyStateMessage, - select: function(node){ - var code = templateHelper.getInsertDictionarySnippet(node.name); - insert(code); - editorService.close(); - }, - close: function (model) { - // close dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - - editorService.treePicker(dictionaryPicker); - - }); - } - - function openQueryBuilderOverlay() { - var queryBuilder = { - submit: function (model) { - var code = templateHelper.getQuerySnippet(model.result.queryExpression); - insert(code); - editorService.close(); - }, - close: function (model) { - // close dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.queryBuilder(queryBuilder); - } - - /* Local functions */ - - function init() { - //we need to load this somewhere, for now its here. - assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); - - if ($routeParams.create) { - - var snippet = "Empty"; - - if($routeParams.snippet) { - snippet = $routeParams.snippet; - } - - codefileResource.getScaffold("partialViewMacros", $routeParams.id, snippet).then(function (partialViewMacro) { - if ($routeParams.name) { - partialViewMacro.name = $routeParams.name; - } - ready(partialViewMacro, false); - }); - - } else { - codefileResource.getByPath('partialViewMacros', $routeParams.id).then(function (partialViewMacro) { - ready(partialViewMacro, true); - }); - } - } - - function ready(partialViewMacro, syncTree) { - - vm.page.loading = false; - vm.partialViewMacro = partialViewMacro; - - //sync state - editorState.set(vm.partialViewMacro); - - if (syncTree) { - navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - } - - // ace configuration - vm.aceOption = { - mode: "razor", - theme: "chrome", - showPrintMargin: false, - advanced: { - fontSize: '14px' - }, - onLoad: function(_editor) { - vm.editor = _editor; - - // initial cursor placement - // Keep cursor in name field if we are create a new template - // else set the cursor at the bottom of the code editor - if(!$routeParams.create) { - $timeout(function(){ - vm.editor.navigateFileEnd(); - vm.editor.focus(); - persistCurrentLocation(); - }); - } - - //change on blur, focus - vm.editor.on("blur", persistCurrentLocation); - vm.editor.on("focus", persistCurrentLocation); - vm.editor.on("change", changeAceEditor); - - } - } - - } - - function insert(str) { - vm.editor.focus(); - vm.editor.moveCursorToPosition(vm.currentPosition); - vm.editor.insert(str); - - // set form state to $dirty - setFormState("dirty"); - } - - function persistCurrentLocation() { - vm.currentPosition = vm.editor.getCursorPosition(); - } - - function changeAceEditor() { - setFormState("dirty"); - } - - function setFormState(state) { - - // get the current form - var currentForm = angularHelper.getCurrentForm($scope); - - // set state - if(state === "dirty") { - currentForm.$setDirty(); - } else if(state === "pristine") { - currentForm.$setPristine(); - } - } - - - init(); + }); } - angular.module("umbraco").controller("Umbraco.Editors.PartialViewMacros.EditController", partialViewMacrosEditController); + function completeSave(saved) { + + localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_partialViewSavedText").then(function (msgValue) { + notificationsService.success(headerValue, msgValue); + }); + }); + + //check if the name changed, if so we need to redirect + if (vm.partialViewMacro.id !== saved.id) { + contentEditingHelper.redirectToRenamedContent(saved.id); + } + else { + vm.page.saveButtonState = "success"; + vm.partialViewMacro = saved; + + //sync state + editorState.set(vm.partialViewMacro); + + // normal tree sync + navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + // clear $dirty state on form + setFormState("pristine"); + } + + } + + function openInsertOverlay() { + var insertOverlay = { + allowedTypes: { + macro: true, + dictionary: true, + umbracoField: true + }, + submit: function (model) { + switch (model.insert.type) { + case "macro": + var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); + insert(macroObject.syntax); + break; + case "dictionary": + var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); + insert(code); + break; + case "umbracoField": + insert(model.insert.umbracoField); + break; + } + editorService.close(); + }, + close: function (oldModel) { + // close the dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.insertCodeSnippet(insertOverlay); + } + + function openMacroOverlay() { + var macroPicker = { + dialogData: {}, + submit: function (model) { + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); + insert(macroObject.syntax); + editorService.close(); + }, + close: function () { + editorService.close(); + vm.editor.focus(); + } + }; + editorService.macroPicker(macroPicker); + } + + + function openPageFieldOverlay() { + var insertFieldEditor = { + submit: function (model) { + insert(model.umbracoField); + editorService.close(); + }, + close: function () { + editorService.close(); + vm.editor.focus(); + } + }; + editorService.insertField(insertFieldEditor); + } + + + function openDictionaryItemOverlay() { + + var labelKeys = [ + "template_insertDictionaryItem", + "emptyStates_emptyDictionaryTree" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + var title = values[0]; + var emptyStateMessage = values[1]; + + var dictionaryPicker = { + section: "translation", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + title: title, + emptyStateMessage: emptyStateMessage, + select: function (node) { + var code = templateHelper.getInsertDictionarySnippet(node.name); + insert(code); + editorService.close(); + }, + close: function (model) { + // close dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + + editorService.treePicker(dictionaryPicker); + + }); + } + + function openQueryBuilderOverlay() { + var queryBuilder = { + submit: function (model) { + var code = templateHelper.getQuerySnippet(model.result.queryExpression); + insert(code); + editorService.close(); + }, + close: function (model) { + // close dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.queryBuilder(queryBuilder); + } + + /* Local functions */ + + function init() { + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); + + if ($routeParams.create) { + + var snippet = "Empty"; + + if ($routeParams.snippet) { + snippet = $routeParams.snippet; + } + + codefileResource.getScaffold("partialViewMacros", $routeParams.id, snippet).then(function (partialViewMacro) { + if ($routeParams.name) { + partialViewMacro.name = $routeParams.name; + } + ready(partialViewMacro, false); + }); + + } else { + codefileResource.getByPath('partialViewMacros', $routeParams.id).then(function (partialViewMacro) { + ready(partialViewMacro, true); + }); + } + } + + function ready(partialViewMacro, syncTree) { + + vm.page.loading = false; + vm.partialViewMacro = partialViewMacro; + + //sync state + editorState.set(vm.partialViewMacro); + + if (syncTree) { + navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + + // ace configuration + vm.aceOption = { + mode: "razor", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px' + }, + onLoad: function (_editor) { + vm.editor = _editor; + + // Set read-only when using runtime mode Production + _editor.setReadOnly(vm.runtimeModeProduction); + + // initial cursor placement + // Keep cursor in name field if we are create a new template + // else set the cursor at the bottom of the code editor + if (!$routeParams.create) { + $timeout(function () { + vm.editor.navigateFileEnd(); + vm.editor.focus(); + persistCurrentLocation(); + }); + } + + //change on blur, focus + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); + vm.editor.on("change", changeAceEditor); + + } + } + + } + + function insert(str) { + vm.editor.focus(); + vm.editor.moveCursorToPosition(vm.currentPosition); + vm.editor.insert(str); + + // set form state to $dirty + setFormState("dirty"); + } + + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + + function changeAceEditor() { + setFormState("dirty"); + } + + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if (state === "dirty") { + currentForm.$setDirty(); + } else if (state === "pristine") { + currentForm.$setPristine(); + } + } + + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.PartialViewMacros.EditController", partialViewMacrosEditController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.html index 36fff3a044..7cffa70aa1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialViewMacros/edit.html @@ -1,81 +1,81 @@
- + -
+ - + - - + + - - - + + + +
+ Content is not editable when using runtime mode Production. +
-
+
-
+
- - + + - - + + -
+
-
+
-
-
+
+
-
-
-
+
+
+
- + - + - - + + - + - + -
- +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.controller.js index 1e21a63755..512c1176c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.controller.js @@ -1,433 +1,438 @@ (function () { - "use strict"; + "use strict"; - function PartialViewsEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper, editorService) { + function PartialViewsEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper, editorService) { - var vm = this; - var infiniteMode = $scope.model && $scope.model.infiniteMode; - var id = infiniteMode ? $scope.model.id : $routeParams.id; - var create = infiniteMode ? $scope.model.create : $routeParams.create; - var snippet = infiniteMode ? $scope.model.snippet : $routeParams.snippet; + var vm = this; + var infiniteMode = $scope.model && $scope.model.infiniteMode; + var id = infiniteMode ? $scope.model.id : $routeParams.id; + var create = infiniteMode ? $scope.model.create : $routeParams.create; + var snippet = infiniteMode ? $scope.model.snippet : $routeParams.snippet; - function close() { - if ($scope.model.close) { - $scope.model.close($scope.model); - } + function close() { + if ($scope.model.close) { + $scope.model.close($scope.model); + } + } + + vm.close = close; + + vm.runtimeModeProduction = Umbraco.Sys.ServerVariables.application.runtimeMode == 'Production'; + + vm.header = {}; + vm.header.editorfor = "visuallyHiddenTexts_newPartialView"; + vm.header.setPageTitle = true; + + vm.page = {}; + vm.page.loading = true; + vm.partialView = {}; + + //menu + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; + + // insert buttons + vm.page.insertDefaultButton = { + labelKey: "general_insert", + addEllipsis: "true", + handler: function () { + vm.openInsertOverlay(); + } + }; + vm.page.insertSubButtons = [ + { + labelKey: "template_insertPageField", + addEllipsis: "true", + handler: function () { + vm.openPageFieldOverlay(); } + }, + { + labelKey: "template_insertMacro", + addEllipsis: "true", + handler: function () { + vm.openMacroOverlay() + } + }, + { + labelKey: "template_insertDictionaryItem", + addEllipsis: "true", + handler: function () { + vm.openDictionaryItemOverlay(); + } + } + ]; - vm.close = close; + //Used to toggle the keyboard shortcut modal + //From a custom keybinding in ace editor - that conflicts with our own to show the dialog + vm.showKeyboardShortcut = false; - vm.header = {}; - vm.header.editorfor = "visuallyHiddenTexts_newPartialView"; - vm.header.setPageTitle = true; + //Keyboard shortcuts for help dialog + vm.page.keyboardShortcutsOverview = []; - vm.page = {}; - vm.page.loading = true; - vm.partialView = {}; + templateHelper.getGeneralShortcuts().then(function (data) { + vm.page.keyboardShortcutsOverview.push(data); + }); + templateHelper.getEditorShortcuts().then(function (data) { + vm.page.keyboardShortcutsOverview.push(data); + }); + templateHelper.getPartialViewEditorShortcuts().then(function (data) { + vm.page.keyboardShortcutsOverview.push(data); + }); - //menu - vm.page.menu = {}; - vm.page.menu.currentSection = appState.getSectionState("currentSection"); - vm.page.menu.currentNode = null; - // insert buttons - vm.page.insertDefaultButton = { - labelKey: "general_insert", - addEllipsis: "true", - handler: function() { - vm.openInsertOverlay(); - } - }; - vm.page.insertSubButtons = [ - { - labelKey: "template_insertPageField", - addEllipsis: "true", - handler: function () { - vm.openPageFieldOverlay(); - } - }, - { - labelKey: "template_insertMacro", - addEllipsis: "true", - handler: function () { - vm.openMacroOverlay() - } - }, - { - labelKey: "template_insertDictionaryItem", - addEllipsis: "true", - handler: function () { - vm.openDictionaryItemOverlay(); - } - } - ]; + // bind functions to view model + vm.save = save; + vm.openPageFieldOverlay = openPageFieldOverlay; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + vm.openMacroOverlay = openMacroOverlay; + vm.openInsertOverlay = openInsertOverlay; - //Used to toggle the keyboard shortcut modal - //From a custom keybinding in ace editor - that conflicts with our own to show the dialog - vm.showKeyboardShortcut = false; + /* Functions bound to view model */ - //Keyboard shortcuts for help dialog - vm.page.keyboardShortcutsOverview = []; + function save() { - templateHelper.getGeneralShortcuts().then(function(data){ - vm.page.keyboardShortcutsOverview.push(data); - }); - templateHelper.getEditorShortcuts().then(function(data){ - vm.page.keyboardShortcutsOverview.push(data); - }); - templateHelper.getPartialViewEditorShortcuts().then(function(data){ - vm.page.keyboardShortcutsOverview.push(data); + vm.page.saveButtonState = "busy"; + vm.partialView.content = vm.editor.getValue(); + + contentEditingHelper.contentEditorPerformSave({ + saveMethod: codefileResource.save, + scope: $scope, + content: vm.partialView, + rebindCallback: function (orignal, saved) { } + }).then(function (saved) { + + localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_partialViewSavedText").then(function (msgValue) { + notificationsService.success(headerValue, msgValue); + }); }); - - // bind functions to view model - vm.save = save; - vm.openPageFieldOverlay = openPageFieldOverlay; - vm.openDictionaryItemOverlay = openDictionaryItemOverlay; - vm.openQueryBuilderOverlay = openQueryBuilderOverlay; - vm.openMacroOverlay = openMacroOverlay; - vm.openInsertOverlay = openInsertOverlay; - - /* Functions bound to view model */ - - function save() { - - vm.page.saveButtonState = "busy"; - vm.partialView.content = vm.editor.getValue(); - - contentEditingHelper.contentEditorPerformSave({ - saveMethod: codefileResource.save, - scope: $scope, - content: vm.partialView, - rebindCallback: function (orignal, saved) {} - }).then(function (saved) { - - localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_partialViewSavedText").then(function(msgValue) { - notificationsService.success(headerValue, msgValue); - }); - }); - - //check if the name changed, if so we need to redirect - if (vm.partialView.id !== saved.id) { - contentEditingHelper.redirectToRenamedContent(saved.id); - } - else { - vm.page.saveButtonState = "success"; - vm.partialView = saved; - - //sync state - editorState.set(vm.partialView); - - // normal tree sync - navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - - // clear $dirty state on form - setFormState("pristine"); - } - }, function (err) { - - vm.page.saveButtonState = "error"; - - localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { - notificationsService.error(headerValue, msgValue); - }); - }); - - }); - + //check if the name changed, if so we need to redirect + if (vm.partialView.id !== saved.id) { + contentEditingHelper.redirectToRenamedContent(saved.id); } + else { + vm.page.saveButtonState = "success"; + vm.partialView = saved; - function openInsertOverlay() { - var insertOverlay = { - allowedTypes: { - macro: true, - dictionary: true, - umbracoField: true - }, - submit: function(model) { + //sync state + editorState.set(vm.partialView); - switch(model.insert.type) { - case "macro": - var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); - insert(macroObject.syntax); - break; - case "dictionary": - var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); - insert(code); - break; - case "umbracoField": - insert(model.insert.umbracoField); - break; - } - editorService.close(); - }, - close: function() { - // close the dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.insertCodeSnippet(insertOverlay); + // normal tree sync + navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + // clear $dirty state on form + setFormState("pristine"); } + }, function (err) { + vm.page.saveButtonState = "error"; - function openMacroOverlay() { - var macroPicker = { - dialogData: {}, - submit: function (model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); - insert(macroObject.syntax); - editorService.close(); - }, - close: function() { - editorService.close(); - vm.editor.focus(); - } - }; - editorService.macroPicker(macroPicker); - } + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); - function openPageFieldOverlay() { - var insertFieldEditor = { - submit: function (model) { - insert(model.umbracoField); - editorService.close(); - }, - close: function () { - editorService.close(); - vm.editor.focus(); - } - }; - editorService.insertField(insertFieldEditor); - } - - - function openDictionaryItemOverlay() { - - var labelKeys = [ - "template_insertDictionaryItem", - "emptyStates_emptyDictionaryTree" - ]; - - localizationService.localizeMany(labelKeys).then(function(values){ - var title = values[0]; - var emptyStateMessage = values[1]; - - var dictionaryItem = { - section: "translation", - treeAlias: "dictionary", - entityType: "dictionary", - multiPicker: false, - title: title, - emptyStateMessage: emptyStateMessage, - select: function(node){ - var code = templateHelper.getInsertDictionarySnippet(node.name); - insert(code); - editorService.close(); - }, - close: function (model) { - // close dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.treePicker(dictionaryItem); - }); - } - - function openQueryBuilderOverlay() { - var queryBuilder = { - title: "Query for content", - submit: function (model) { - var code = templateHelper.getQuerySnippet(model.result.queryExpression); - insert(code); - editorService.close(); - }, - close: function () { - // close dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.queryBuilder(queryBuilder); - } - - /* Local functions */ - - function init() { - //we need to load this somewhere, for now its here. - assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); - - if (create) { - - if (!snippet) { - snippet = "Empty"; - } - - codefileResource.getScaffold("partialViews", id, snippet).then(function (partialView) { - ready(partialView, false); - }); - - } else { - codefileResource.getByPath('partialViews', id).then(function (partialView) { - ready(partialView, true); - }); - } - - } - - function ready(partialView, syncTree) { - - vm.page.loading = false; - vm.partialView = partialView; - - //sync state - editorState.set(vm.partialView); - - if (!infiniteMode && syncTree) { - navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - } - - // ace configuration - vm.aceOption = { - mode: "razor", - theme: "chrome", - showPrintMargin: false, - advanced: { - fontSize: '14px' - }, - onLoad: function(_editor) { - vm.editor = _editor; - - //Update the auto-complete method to use ctrl+alt+space - _editor.commands.bindKey("ctrl-alt-space", "startAutocomplete"); - - //Unassigns the keybinding (That was previously auto-complete) - //As conflicts with our own tree search shortcut - _editor.commands.bindKey("ctrl-space", null); - - // Assign new keybinding - _editor.commands.addCommands([ - //Disable (alt+shift+K) - //Conflicts with our own show shortcuts dialog - this overrides it - { - name: 'unSelectOrFindPrevious', - bindKey: 'Alt-Shift-K', - exec: function () { - //Toggle the show keyboard shortcuts overlay - $scope.$apply(function () { - vm.showKeyboardShortcut = !vm.showKeyboardShortcut; - }); - }, - readOnly: true - }, - { - name: 'insertUmbracoValue', - bindKey: 'Alt-Shift-V', - exec: function () { - $scope.$apply(function () { - openPageFieldOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertDictionary', - bindKey: 'Alt-Shift-D', - exec: function () { - $scope.$apply(function () { - openDictionaryItemOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertUmbracoMacro', - bindKey: 'Alt-Shift-M', - exec: function () { - $scope.$apply(function () { - openMacroOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertQuery', - bindKey: 'Alt-Shift-Q', - exec: function () { - $scope.$apply(function () { - openQueryBuilderOverlay(); - }); - }, - readOnly: true - } - - ]); - - // initial cursor placement - // Keep cursor in name field if we are create a new template - // else set the cursor at the bottom of the code editor - if(!create) { - $timeout(function(){ - vm.editor.navigateFileEnd(); - vm.editor.focus(); - persistCurrentLocation(); - }); - } - - //change on blur, focus - vm.editor.on("blur", persistCurrentLocation); - vm.editor.on("focus", persistCurrentLocation); - vm.editor.on("change", changeAceEditor); - - } - } - - } - - function insert(str) { - vm.editor.focus(); - vm.editor.moveCursorToPosition(vm.currentPosition); - vm.editor.insert(str); - - // set form state to $dirty - setFormState("dirty"); - } - - function persistCurrentLocation() { - vm.currentPosition = vm.editor.getCursorPosition(); - } - - function changeAceEditor() { - setFormState("dirty"); - } - - function setFormState(state) { - - // get the current form - var currentForm = angularHelper.getCurrentForm($scope); - - // set state - if(state === "dirty") { - currentForm.$setDirty(); - } else if(state === "pristine") { - currentForm.$setPristine(); - } - } - - - init(); + }); } - angular.module("umbraco").controller("Umbraco.Editors.PartialViews.EditController", PartialViewsEditController); + function openInsertOverlay() { + var insertOverlay = { + allowedTypes: { + macro: true, + dictionary: true, + umbracoField: true + }, + submit: function (model) { + + switch (model.insert.type) { + case "macro": + var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); + insert(macroObject.syntax); + break; + case "dictionary": + var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); + insert(code); + break; + case "umbracoField": + insert(model.insert.umbracoField); + break; + } + editorService.close(); + }, + close: function () { + // close the dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.insertCodeSnippet(insertOverlay); + } + + + function openMacroOverlay() { + var macroPicker = { + dialogData: {}, + submit: function (model) { + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); + insert(macroObject.syntax); + editorService.close(); + }, + close: function () { + editorService.close(); + vm.editor.focus(); + } + }; + editorService.macroPicker(macroPicker); + } + + function openPageFieldOverlay() { + var insertFieldEditor = { + submit: function (model) { + insert(model.umbracoField); + editorService.close(); + }, + close: function () { + editorService.close(); + vm.editor.focus(); + } + }; + editorService.insertField(insertFieldEditor); + } + + + function openDictionaryItemOverlay() { + + var labelKeys = [ + "template_insertDictionaryItem", + "emptyStates_emptyDictionaryTree" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + var title = values[0]; + var emptyStateMessage = values[1]; + + var dictionaryItem = { + section: "translation", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + title: title, + emptyStateMessage: emptyStateMessage, + select: function (node) { + var code = templateHelper.getInsertDictionarySnippet(node.name); + insert(code); + editorService.close(); + }, + close: function (model) { + // close dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.treePicker(dictionaryItem); + }); + } + + function openQueryBuilderOverlay() { + var queryBuilder = { + title: "Query for content", + submit: function (model) { + var code = templateHelper.getQuerySnippet(model.result.queryExpression); + insert(code); + editorService.close(); + }, + close: function () { + // close dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.queryBuilder(queryBuilder); + } + + /* Local functions */ + + function init() { + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); + + if (create) { + + if (!snippet) { + snippet = "Empty"; + } + + codefileResource.getScaffold("partialViews", id, snippet).then(function (partialView) { + ready(partialView, false); + }); + + } else { + codefileResource.getByPath('partialViews', id).then(function (partialView) { + ready(partialView, true); + }); + } + + } + + function ready(partialView, syncTree) { + + vm.page.loading = false; + vm.partialView = partialView; + + //sync state + editorState.set(vm.partialView); + + if (!infiniteMode && syncTree) { + navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + + // ace configuration + vm.aceOption = { + mode: "razor", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px' + }, + onLoad: function (_editor) { + vm.editor = _editor; + + // Set read-only when using runtime mode Production + _editor.setReadOnly(vm.runtimeModeProduction); + + //Update the auto-complete method to use ctrl+alt+space + _editor.commands.bindKey("ctrl-alt-space", "startAutocomplete"); + + //Unassigns the keybinding (That was previously auto-complete) + //As conflicts with our own tree search shortcut + _editor.commands.bindKey("ctrl-space", null); + + // Assign new keybinding + _editor.commands.addCommands([ + //Disable (alt+shift+K) + //Conflicts with our own show shortcuts dialog - this overrides it + { + name: 'unSelectOrFindPrevious', + bindKey: 'Alt-Shift-K', + exec: function () { + //Toggle the show keyboard shortcuts overlay + $scope.$apply(function () { + vm.showKeyboardShortcut = !vm.showKeyboardShortcut; + }); + }, + readOnly: true + }, + { + name: 'insertUmbracoValue', + bindKey: 'Alt-Shift-V', + exec: function () { + $scope.$apply(function () { + openPageFieldOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertDictionary', + bindKey: 'Alt-Shift-D', + exec: function () { + $scope.$apply(function () { + openDictionaryItemOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertUmbracoMacro', + bindKey: 'Alt-Shift-M', + exec: function () { + $scope.$apply(function () { + openMacroOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertQuery', + bindKey: 'Alt-Shift-Q', + exec: function () { + $scope.$apply(function () { + openQueryBuilderOverlay(); + }); + }, + readOnly: true + } + + ]); + + // initial cursor placement + // Keep cursor in name field if we are create a new template + // else set the cursor at the bottom of the code editor + if (!create) { + $timeout(function () { + vm.editor.navigateFileEnd(); + vm.editor.focus(); + persistCurrentLocation(); + }); + } + + //change on blur, focus + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); + vm.editor.on("change", changeAceEditor); + + } + } + + } + + function insert(str) { + vm.editor.focus(); + vm.editor.moveCursorToPosition(vm.currentPosition); + vm.editor.insert(str); + + // set form state to $dirty + setFormState("dirty"); + } + + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + + function changeAceEditor() { + setFormState("dirty"); + } + + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if (state === "dirty") { + currentForm.$setDirty(); + } else if (state === "pristine") { + currentForm.$setPristine(); + } + } + + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.PartialViews.EditController", PartialViewsEditController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.html index 4a4d01385e..e1eb195545 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialViews/edit.html @@ -1,97 +1,98 @@
- + -
+ - + - - + + - + - - + + +
+ Content is not editable when using runtime mode Production. +
-
+
-
+
- - + + - - + + -
+
-
+
-
-
+
+
-
-
- -
+ + - +
- - - - + - + + + + - - - - - - + - + + -
- + + + + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 99f11f9913..1e14cf1e87 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,723 +1,728 @@ (function () { - "use strict"; + "use strict"; - function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper, templateHelper, editorService) { + function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper, templateHelper, editorService) { - var vm = this; - var oldMasterTemplateAlias = null; - var infiniteMode = $scope.model && $scope.model.infiniteMode; - var id = infiniteMode ? $scope.model.id : $routeParams.id; - var create = infiniteMode ? $scope.model.create : $routeParams.create; + var vm = this; + var oldMasterTemplateAlias = null; + var infiniteMode = $scope.model && $scope.model.infiniteMode; + var id = infiniteMode ? $scope.model.id : $routeParams.id; + var create = infiniteMode ? $scope.model.create : $routeParams.create; - vm.header = {}; - vm.header.editorfor = "template_template"; - vm.header.setPageTitle = true; + vm.runtimeModeProduction = Umbraco.Sys.ServerVariables.application.runtimeMode == 'Production'; - vm.page = {}; - vm.page.loading = true; - vm.templates = []; + vm.header = {}; + vm.header.editorfor = "template_template"; + vm.header.setPageTitle = true; - //menu - vm.page.menu = {}; - vm.page.menu.currentSection = appState.getSectionState("currentSection"); - vm.page.menu.currentNode = null; + vm.page = {}; + vm.page.loading = true; + vm.templates = []; - // insert buttons - vm.page.insertDefaultButton = { - labelKey: "general_insert", - addEllipsis: "true", - handler: function () { - vm.openInsertOverlay(); - } - }; - vm.page.insertSubButtons = [ - { - labelKey: "template_insertPageField", - addEllipsis: "true", - handler: function () { - vm.openPageFieldOverlay(); - } - }, - { - labelKey: "template_insertPartialView", - addEllipsis: "true", - handler: function () { - vm.openPartialOverlay(); - } - }, - { - labelKey: "template_insertDictionaryItem", - addEllipsis: "true", - handler: function () { - vm.openDictionaryItemOverlay(); - } - }, - { - labelKey: "template_insertMacro", - addEllipsis: "true", - handler: function () { - vm.openMacroOverlay() - } - } - ]; + //menu + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; - //Used to toggle the keyboard shortcut modal - //From a custom keybinding in ace editor - that conflicts with our own to show the dialog - vm.showKeyboardShortcut = false; + // insert buttons + vm.page.insertDefaultButton = { + labelKey: "general_insert", + addEllipsis: "true", + handler: function () { + vm.openInsertOverlay(); + } + }; + vm.page.insertSubButtons = [ + { + labelKey: "template_insertPageField", + addEllipsis: "true", + handler: function () { + vm.openPageFieldOverlay(); + } + }, + { + labelKey: "template_insertPartialView", + addEllipsis: "true", + handler: function () { + vm.openPartialOverlay(); + } + }, + { + labelKey: "template_insertDictionaryItem", + addEllipsis: "true", + handler: function () { + vm.openDictionaryItemOverlay(); + } + }, + { + labelKey: "template_insertMacro", + addEllipsis: "true", + handler: function () { + vm.openMacroOverlay() + } + } + ]; - //Keyboard shortcuts for help dialog - vm.page.keyboardShortcutsOverview = []; + //Used to toggle the keyboard shortcut modal + //From a custom keybinding in ace editor - that conflicts with our own to show the dialog + vm.showKeyboardShortcut = false; - templateHelper.getGeneralShortcuts().then(function (data) { - vm.page.keyboardShortcutsOverview.push(data); - }); - templateHelper.getEditorShortcuts().then(function (data) { - vm.page.keyboardShortcutsOverview.push(data); - }); - templateHelper.getTemplateEditorShortcuts().then(function (data) { - vm.page.keyboardShortcutsOverview.push(data); + //Keyboard shortcuts for help dialog + vm.page.keyboardShortcutsOverview = []; + + templateHelper.getGeneralShortcuts().then(function (data) { + vm.page.keyboardShortcutsOverview.push(data); + }); + templateHelper.getEditorShortcuts().then(function (data) { + vm.page.keyboardShortcutsOverview.push(data); + }); + templateHelper.getTemplateEditorShortcuts().then(function (data) { + vm.page.keyboardShortcutsOverview.push(data); + }); + + vm.save = function (suppressNotification) { + vm.page.saveButtonState = "busy"; + + vm.template.content = vm.editor.getValue(); + + contentEditingHelper.contentEditorPerformSave({ + saveMethod: templateResource.save, + scope: $scope, + content: vm.template, + rebindCallback: function (orignal, saved) { } + }).then(function (saved) { + + if (!suppressNotification) { + localizationService.localizeMany(["speechBubbles_templateSavedHeader", "speechBubbles_templateSavedText"]).then(function (data) { + var header = data[0]; + var message = data[1]; + notificationsService.success(header, message); + }); + } + + vm.page.saveButtonState = "success"; + vm.template = saved; + + //sync state + if (!infiniteMode) { + editorState.set(vm.template); + } + + // sync tree + // if master template alias has changed move the node to it's new location + if (!infiniteMode && oldMasterTemplateAlias !== vm.template.masterTemplateAlias) { + + // When creating a new template the id is -1. Make sure We don't remove the root node. + if (vm.page.menu.currentNode.id !== "-1") { + // move node to new location in tree + //first we need to remove the node that we're working on + treeService.removeNode(vm.page.menu.currentNode); + } + + // update stored alias to the new one so the node won't move again unless the alias is changed again + oldMasterTemplateAlias = vm.template.masterTemplateAlias; + + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true, activate: true }).then(function (args) { + vm.page.menu.currentNode = args.node; + }); + + } else { + + // normal tree sync + if (!infiniteMode) { + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + + } + + // clear $dirty state on form + setFormState("pristine"); + + if (infiniteMode) { + submit(); + } + + + }, function (err) { + if (suppressNotification) { + vm.page.saveButtonState = "error"; + + localizationService.localizeMany(["speechBubbles_validationFailedHeader", "speechBubbles_validationFailedMessage"]).then(function (data) { + var header = data[0]; + var message = data[1]; + notificationsService.error(header, message); + }); + } + }); + + }; + + vm.init = function () { + + // we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); + + // load templates - used in the master template picker + templateResource.getAll() + .then(function (templates) { + vm.templates = templates; }); - vm.save = function (suppressNotification) { - vm.page.saveButtonState = "busy"; + if (create) { + templateResource.getScaffold((id)).then(function (template) { + vm.ready(template); + }); + } else { + templateResource.getById(id).then(function (template) { + vm.ready(template); + }); + } - vm.template.content = vm.editor.getValue(); - - contentEditingHelper.contentEditorPerformSave({ - saveMethod: templateResource.save, - scope: $scope, - content: vm.template, - rebindCallback: function (orignal, saved) { } - }).then(function (saved) { - - if (!suppressNotification) { - localizationService.localizeMany(["speechBubbles_templateSavedHeader", "speechBubbles_templateSavedText"]).then(function (data) { - var header = data[0]; - var message = data[1]; - notificationsService.success(header, message); - }); - } - - vm.page.saveButtonState = "success"; - vm.template = saved; - - //sync state - if (!infiniteMode) { - editorState.set(vm.template); - } - - // sync tree - // if master template alias has changed move the node to it's new location - if (!infiniteMode && oldMasterTemplateAlias !== vm.template.masterTemplateAlias) { - - // When creating a new template the id is -1. Make sure We don't remove the root node. - if (vm.page.menu.currentNode.id !== "-1") { - // move node to new location in tree - //first we need to remove the node that we're working on - treeService.removeNode(vm.page.menu.currentNode); - } - - // update stored alias to the new one so the node won't move again unless the alias is changed again - oldMasterTemplateAlias = vm.template.masterTemplateAlias; - - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true, activate: true }).then(function (args) { - vm.page.menu.currentNode = args.node; - }); - - } else { - - // normal tree sync - if (!infiniteMode) { - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - } - - } - - // clear $dirty state on form - setFormState("pristine"); - - if (infiniteMode) { - submit(); - } + }; - }, function (err) { - if (suppressNotification) { - vm.page.saveButtonState = "error"; + vm.ready = function (template) { + vm.page.loading = false; + vm.template = template; - localizationService.localizeMany(["speechBubbles_validationFailedHeader", "speechBubbles_validationFailedMessage"]).then(function (data) { - var header = data[0]; - var message = data[1]; - notificationsService.error(header, message); - }); - } + // if this is a new template, bind to the blur event on the name + if (create) { + $timeout(function () { + var nameField = $('[data-element="editor-name-field"]'); + if (nameField) { + nameField.on('blur', function (event) { + if (event.target.value) { + vm.save(true); + } }); + } + }); + } + // sync state + if (!infiniteMode) { + editorState.set(vm.template); + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + + // save state of master template to use for comparison when syncing the tree on save + oldMasterTemplateAlias = Utilities.copy(template.masterTemplateAlias); + + // ace configuration + vm.aceOption = { + mode: "razor", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px', + enableSnippets: false, //The Razor mode snippets are awful (Need a way to override these) + enableBasicAutocompletion: true, + enableLiveAutocompletion: false + }, + onLoad: function (_editor) { + vm.editor = _editor; + + // Set read-only when using runtime mode Production + _editor.setReadOnly(vm.runtimeModeProduction); + + // Update the auto-complete method to use ctrl+alt+space + _editor.commands.bindKey("ctrl-alt-space", "startAutocomplete"); + + // Unassigns the keybinding (That was previously auto-complete) + // As conflicts with our own tree search shortcut + _editor.commands.bindKey("ctrl-space", null); + + // Assign new keybinding + _editor.commands.addCommands([ + // Disable (alt+shift+K) + // Conflicts with our own show shortcuts dialog - this overrides it + { + name: 'unSelectOrFindPrevious', + bindKey: 'Alt-Shift-K', + exec: function () { + // Toggle the show keyboard shortcuts overlay + $scope.$apply(function () { + vm.showKeyboardShortcut = !vm.showKeyboardShortcut; + }); + + }, + readOnly: true + }, + { + name: 'insertUmbracoValue', + bindKey: 'Alt-Shift-V', + exec: function () { + $scope.$apply(function () { + openPageFieldOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertPartialView', + bindKey: 'Alt-Shift-P', + exec: function () { + $scope.$apply(function () { + openPartialOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertDictionary', + bindKey: 'Alt-Shift-D', + exec: function () { + $scope.$apply(function () { + openDictionaryItemOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertUmbracoMacro', + bindKey: 'Alt-Shift-M', + exec: function () { + $scope.$apply(function () { + openMacroOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertQuery', + bindKey: 'Alt-Shift-Q', + exec: function () { + $scope.$apply(function () { + openQueryBuilderOverlay(); + }); + }, + readOnly: true + }, + { + name: 'insertSection', + bindKey: 'Alt-Shift-S', + exec: function () { + $scope.$apply(function () { + openSectionsOverlay(); + }); + }, + readOnly: true + }, + { + name: 'chooseMasterTemplate', + bindKey: 'Alt-Shift-T', + exec: function () { + $scope.$apply(function () { + openMasterTemplateOverlay(); + }); + }, + readOnly: true + } + + ]); + + // initial cursor placement + // Keep cursor in name field if we are create a new template + // else set the cursor at the bottom of the code editor + if (!create) { + $timeout(function () { + vm.editor.navigateFileEnd(); + vm.editor.focus(); + persistCurrentLocation(); + }); + } + + // change on blur, focus + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); + vm.editor.on("change", changeAceEditor); + } + } + + }; + + vm.openPageFieldOverlay = openPageFieldOverlay; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + vm.openMacroOverlay = openMacroOverlay; + vm.openInsertOverlay = openInsertOverlay; + vm.openSectionsOverlay = openSectionsOverlay; + vm.openPartialOverlay = openPartialOverlay; + vm.openMasterTemplateOverlay = openMasterTemplateOverlay; + vm.selectMasterTemplate = selectMasterTemplate; + vm.getMasterTemplateName = getMasterTemplateName; + vm.removeMasterTemplate = removeMasterTemplate; + vm.closeShortcuts = closeShortcuts; + vm.submit = submit; + vm.close = close; + + function openInsertOverlay() { + var insertOverlay = { + allowedTypes: { + macro: true, + dictionary: true, + partial: true, + umbracoField: true + }, + submit: function (model) { + switch (model.insert.type) { + case "macro": + var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); + insert(macroObject.syntax); + break; + case "dictionary": + var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); + insert(code); + break; + case "partial": + var code = templateHelper.getInsertPartialSnippet(model.insert.node.parentId, model.insert.node.name); + insert(code); + break; + case "umbracoField": + insert(model.insert.umbracoField); + break; + } + editorService.close(); + }, + close: function (oldModel) { + // close the dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.insertCodeSnippet(insertOverlay); + } + + function openMacroOverlay() { + var macroPicker = { + dialogData: {}, + submit: function (model) { + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); + insert(macroObject.syntax); + editorService.close(); + }, + close: function () { + editorService.close(); + vm.editor.focus(); + } + }; + editorService.macroPicker(macroPicker); + } + + function openPageFieldOverlay() { + var insertFieldEditor = { + submit: function (model) { + insert(model.umbracoField); + editorService.close(); + }, + close: function () { + editorService.close(); + vm.editor.focus(); + } + }; + editorService.insertField(insertFieldEditor); + } + + + function openDictionaryItemOverlay() { + + var labelKeys = [ + "template_insertDictionaryItem", + "emptyStates_emptyDictionaryTree" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + var title = values[0]; + var emptyStateMessage = values[1]; + + var dictionaryItem = { + section: "translation", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + title: title, + emptyStateMessage: emptyStateMessage, + select: function (node) { + var code = templateHelper.getInsertDictionarySnippet(node.name); + insert(code); + editorService.close(); + }, + close: function (model) { + // close dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } }; - vm.init = function () { + editorService.treePicker(dictionaryItem); - // we need to load this somewhere, for now its here. - assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); - - // load templates - used in the master template picker - templateResource.getAll() - .then(function (templates) { - vm.templates = templates; - }); - - if (create) { - templateResource.getScaffold((id)).then(function (template) { - vm.ready(template); - }); - } else { - templateResource.getById(id).then(function (template) { - vm.ready(template); - }); - } - - }; - - - vm.ready = function (template) { - vm.page.loading = false; - vm.template = template; - - // if this is a new template, bind to the blur event on the name - if (create) { - $timeout(function () { - var nameField = $('[data-element="editor-name-field"]'); - if (nameField) { - nameField.on('blur', function (event) { - if (event.target.value) { - vm.save(true); - } - }); - } - }); - } - - // sync state - if (!infiniteMode) { - editorState.set(vm.template); - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - } - - // save state of master template to use for comparison when syncing the tree on save - oldMasterTemplateAlias = Utilities.copy(template.masterTemplateAlias); - - // ace configuration - vm.aceOption = { - mode: "razor", - theme: "chrome", - showPrintMargin: false, - advanced: { - fontSize: '14px', - enableSnippets: false, //The Razor mode snippets are awful (Need a way to override these) - enableBasicAutocompletion: true, - enableLiveAutocompletion: false - }, - onLoad: function (_editor) { - vm.editor = _editor; - - //Update the auto-complete method to use ctrl+alt+space - _editor.commands.bindKey("ctrl-alt-space", "startAutocomplete"); - - // Unassigns the keybinding (That was previously auto-complete) - // As conflicts with our own tree search shortcut - _editor.commands.bindKey("ctrl-space", null); - - // Assign new keybinding - _editor.commands.addCommands([ - // Disable (alt+shift+K) - // Conflicts with our own show shortcuts dialog - this overrides it - { - name: 'unSelectOrFindPrevious', - bindKey: 'Alt-Shift-K', - exec: function () { - // Toggle the show keyboard shortcuts overlay - $scope.$apply(function () { - vm.showKeyboardShortcut = !vm.showKeyboardShortcut; - }); - - }, - readOnly: true - }, - { - name: 'insertUmbracoValue', - bindKey: 'Alt-Shift-V', - exec: function () { - $scope.$apply(function () { - openPageFieldOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertPartialView', - bindKey: 'Alt-Shift-P', - exec: function () { - $scope.$apply(function () { - openPartialOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertDictionary', - bindKey: 'Alt-Shift-D', - exec: function () { - $scope.$apply(function () { - openDictionaryItemOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertUmbracoMacro', - bindKey: 'Alt-Shift-M', - exec: function () { - $scope.$apply(function () { - openMacroOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertQuery', - bindKey: 'Alt-Shift-Q', - exec: function () { - $scope.$apply(function () { - openQueryBuilderOverlay(); - }); - }, - readOnly: true - }, - { - name: 'insertSection', - bindKey: 'Alt-Shift-S', - exec: function () { - $scope.$apply(function () { - openSectionsOverlay(); - }); - }, - readOnly: true - }, - { - name: 'chooseMasterTemplate', - bindKey: 'Alt-Shift-T', - exec: function () { - $scope.$apply(function () { - openMasterTemplateOverlay(); - }); - }, - readOnly: true - } - - ]); - - // initial cursor placement - // Keep cursor in name field if we are create a new template - // else set the cursor at the bottom of the code editor - if (!create) { - $timeout(function () { - vm.editor.navigateFileEnd(); - vm.editor.focus(); - persistCurrentLocation(); - }); - } - - // change on blur, focus - vm.editor.on("blur", persistCurrentLocation); - vm.editor.on("focus", persistCurrentLocation); - vm.editor.on("change", changeAceEditor); - } - } - - }; - - vm.openPageFieldOverlay = openPageFieldOverlay; - vm.openDictionaryItemOverlay = openDictionaryItemOverlay; - vm.openQueryBuilderOverlay = openQueryBuilderOverlay; - vm.openMacroOverlay = openMacroOverlay; - vm.openInsertOverlay = openInsertOverlay; - vm.openSectionsOverlay = openSectionsOverlay; - vm.openPartialOverlay = openPartialOverlay; - vm.openMasterTemplateOverlay = openMasterTemplateOverlay; - vm.selectMasterTemplate = selectMasterTemplate; - vm.getMasterTemplateName = getMasterTemplateName; - vm.removeMasterTemplate = removeMasterTemplate; - vm.closeShortcuts = closeShortcuts; - vm.submit = submit; - vm.close = close; - - function openInsertOverlay() { - var insertOverlay = { - allowedTypes: { - macro: true, - dictionary: true, - partial: true, - umbracoField: true - }, - submit: function (model) { - switch (model.insert.type) { - case "macro": - var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); - insert(macroObject.syntax); - break; - case "dictionary": - var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); - insert(code); - break; - case "partial": - var code = templateHelper.getInsertPartialSnippet(model.insert.node.parentId, model.insert.node.name); - insert(code); - break; - case "umbracoField": - insert(model.insert.umbracoField); - break; - } - editorService.close(); - }, - close: function (oldModel) { - // close the dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.insertCodeSnippet(insertOverlay); - } - - function openMacroOverlay() { - var macroPicker = { - dialogData: {}, - submit: function (model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); - insert(macroObject.syntax); - editorService.close(); - }, - close: function () { - editorService.close(); - vm.editor.focus(); - } - }; - editorService.macroPicker(macroPicker); - } - - function openPageFieldOverlay() { - var insertFieldEditor = { - submit: function (model) { - insert(model.umbracoField); - editorService.close(); - }, - close: function () { - editorService.close(); - vm.editor.focus(); - } - }; - editorService.insertField(insertFieldEditor); - } - - - function openDictionaryItemOverlay() { - - var labelKeys = [ - "template_insertDictionaryItem", - "emptyStates_emptyDictionaryTree" - ]; - - localizationService.localizeMany(labelKeys).then(function (values) { - var title = values[0]; - var emptyStateMessage = values[1]; - - var dictionaryItem = { - section: "translation", - treeAlias: "dictionary", - entityType: "dictionary", - multiPicker: false, - title: title, - emptyStateMessage: emptyStateMessage, - select: function (node) { - var code = templateHelper.getInsertDictionarySnippet(node.name); - insert(code); - editorService.close(); - }, - close: function (model) { - // close dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - - editorService.treePicker(dictionaryItem); - - }); - - } - - function openPartialOverlay() { - - localizationService.localize("template_insertPartialView").then(function (value) { - var title = value; - - var partialItem = { - section: "settings", - treeAlias: "partialViews", - entityType: "partialView", - multiPicker: false, - title: title, - filter: function (i) { - if (i.name.indexOf(".cshtml") === -1 && i.name.indexOf(".vbhtml") === -1) { - return true; - } - }, - filterCssClass: "not-allowed", - select: function (node) { - var code = templateHelper.getInsertPartialSnippet(node.parentId, node.name); - insert(code); - editorService.close(); - }, - close: function (model) { - // close dialog - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - - editorService.treePicker(partialItem); - }); - } - - function openQueryBuilderOverlay() { - var queryBuilder = { - submit: function (model) { - var code = templateHelper.getQuerySnippet(model.result.queryExpression); - insert(code); - editorService.close(); - }, - close: function () { - editorService.close(); - // focus editor - vm.editor.focus(); - } - }; - editorService.queryBuilder(queryBuilder); - } - - - function openSectionsOverlay() { - var templateSections = { - isMaster: vm.template.isMasterTemplate, - submit: function (model) { - - if (model.insertType === 'renderBody') { - var code = templateHelper.getRenderBodySnippet(); - insert(code); - } - - if (model.insertType === 'renderSection') { - var code = templateHelper.getRenderSectionSnippet(model.renderSectionName, model.mandatoryRenderSection); - insert(code); - } - - if (model.insertType === 'addSection') { - var code = templateHelper.getAddSectionSnippet(model.sectionName); - wrap(code); - } - - editorService.close(); - - }, - close: function (model) { - editorService.close(); - vm.editor.focus(); - } - } - editorService.templateSections(templateSections); - } - - function openMasterTemplateOverlay() { - - // make collection of available master templates - var availableMasterTemplates = []; - - // filter out the current template and the selected master template - vm.templates.forEach(function (template) { - if (template.alias !== vm.template.alias && template.alias !== vm.template.masterTemplateAlias) { - var templatePathArray = template.path.split(','); - // filter descendant templates of current template - if (templatePathArray.indexOf(String(vm.template.id)) === -1) { - availableMasterTemplates.push(template); - } - } - }); - - const editor = { - filterCssClass: 'not-allowed', - filter: item => !availableMasterTemplates.some(template => template.id == item.id), - submit: model => { - const template = model.selection[0]; - if (template && template.alias) { - vm.template.masterTemplateAlias = template.alias; - setLayout(template.alias + ".cshtml"); - } else { - vm.template.masterTemplateAlias = null; - setLayout(null); - } - editorService.close(); - }, - close: () => editorService.close() - } - - localizationService.localize("template_mastertemplate").then(title => { - editor.title = title; - - const currentTemplate = vm.templates.find(template => template.alias == vm.template.masterTemplateAlias); - if (currentTemplate) { - editor.currentNode = { - path: currentTemplate.path - }; - } - - editorService.templatePicker(editor); - }); - - } - - function selectMasterTemplate(template) { - - if (template && template.alias) { - vm.template.masterTemplateAlias = template.alias; - setLayout(template.alias + ".cshtml"); - } else { - vm.template.masterTemplateAlias = null; - setLayout(null); - } - - } - - function getMasterTemplateName(masterTemplateAlias, templates) { - if (masterTemplateAlias) { - var templateName = ""; - templates.forEach(function (template) { - if (template.alias === masterTemplateAlias) { - templateName = template.name; - } - }); - return templateName; - } - } - - function removeMasterTemplate() { - - vm.template.masterTemplateAlias = null; - - // call set layout with no paramters to set layout to null - setLayout(); - - } - - function setLayout(templatePath) { - - var templateCode = vm.editor.getValue(); - var newValue = templatePath; - var layoutDefRegex = new RegExp("(@{[\\s\\S]*?Layout\\s*?=\\s*?)(\"[^\"]*?\"|null)(;[\\s\\S]*?})", "gi"); - - if (newValue !== undefined && newValue !== "") { - if (layoutDefRegex.test(templateCode)) { - // Declaration exists, so just update it - templateCode = templateCode.replace(layoutDefRegex, "$1\"" + newValue + "\"$3"); - } else { - // Declaration doesn't exist, so prepend to start of doc - // TODO: Maybe insert at the cursor position, rather than just at the top of the doc? - templateCode = "@{\n\tLayout = \"" + newValue + "\";\n}\n" + templateCode; - } - } else { - if (layoutDefRegex.test(templateCode)) { - // Declaration exists, so just update it - templateCode = templateCode.replace(layoutDefRegex, "$1null$3"); - } - } - - vm.editor.setValue(templateCode); - vm.editor.clearSelection(); - vm.editor.navigateFileStart(); - - vm.editor.focus(); - // set form state to $dirty - setFormState("dirty"); - - } - - - function insert(str) { - vm.editor.focus(); - vm.editor.moveCursorToPosition(vm.currentPosition); - vm.editor.insert(str); - - // set form state to $dirty - setFormState("dirty"); - } - - function wrap(str) { - - var selectedContent = vm.editor.session.getTextRange(vm.editor.getSelectionRange()); - str = str.replace("{0}", selectedContent); - vm.editor.insert(str); - vm.editor.focus(); - - // set form state to $dirty - setFormState("dirty"); - } - - function persistCurrentLocation() { - vm.currentPosition = vm.editor.getCursorPosition(); - } - - function changeAceEditor() { - setFormState("dirty"); - } - - function setFormState(state) { - - // get the current form - var currentForm = angularHelper.getCurrentForm($scope); - - // set state - if (state === "dirty") { - currentForm.$setDirty(); - } else if (state === "pristine") { - currentForm.$setPristine(); - } - } - - function closeShortcuts() { - vm.showKeyboardShortcut = false; - } - - function submit() { - if ($scope.model.submit) { - $scope.model.template = vm.template; - $scope.model.submit($scope.model); - } - } - - function close() { - if ($scope.model.close) { - $scope.model.close(); - } - } - - vm.init(); + }); } - angular.module("umbraco").controller("Umbraco.Editors.Templates.EditController", TemplatesEditController); + function openPartialOverlay() { + + localizationService.localize("template_insertPartialView").then(function (value) { + var title = value; + + var partialItem = { + section: "settings", + treeAlias: "partialViews", + entityType: "partialView", + multiPicker: false, + title: title, + filter: function (i) { + if (i.name.indexOf(".cshtml") === -1 && i.name.indexOf(".vbhtml") === -1) { + return true; + } + }, + filterCssClass: "not-allowed", + select: function (node) { + var code = templateHelper.getInsertPartialSnippet(node.parentId, node.name); + insert(code); + editorService.close(); + }, + close: function (model) { + // close dialog + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + + editorService.treePicker(partialItem); + }); + } + + function openQueryBuilderOverlay() { + var queryBuilder = { + submit: function (model) { + var code = templateHelper.getQuerySnippet(model.result.queryExpression); + insert(code); + editorService.close(); + }, + close: function () { + editorService.close(); + // focus editor + vm.editor.focus(); + } + }; + editorService.queryBuilder(queryBuilder); + } + + + function openSectionsOverlay() { + var templateSections = { + isMaster: vm.template.isMasterTemplate, + submit: function (model) { + + if (model.insertType === 'renderBody') { + var code = templateHelper.getRenderBodySnippet(); + insert(code); + } + + if (model.insertType === 'renderSection') { + var code = templateHelper.getRenderSectionSnippet(model.renderSectionName, model.mandatoryRenderSection); + insert(code); + } + + if (model.insertType === 'addSection') { + var code = templateHelper.getAddSectionSnippet(model.sectionName); + wrap(code); + } + + editorService.close(); + + }, + close: function (model) { + editorService.close(); + vm.editor.focus(); + } + } + editorService.templateSections(templateSections); + } + + function openMasterTemplateOverlay() { + + // make collection of available master templates + var availableMasterTemplates = []; + + // filter out the current template and the selected master template + vm.templates.forEach(function (template) { + if (template.alias !== vm.template.alias && template.alias !== vm.template.masterTemplateAlias) { + var templatePathArray = template.path.split(','); + // filter descendant templates of current template + if (templatePathArray.indexOf(String(vm.template.id)) === -1) { + availableMasterTemplates.push(template); + } + } + }); + + const editor = { + filterCssClass: 'not-allowed', + filter: item => !availableMasterTemplates.some(template => template.id == item.id), + submit: model => { + const template = model.selection[0]; + if (template && template.alias) { + vm.template.masterTemplateAlias = template.alias; + setLayout(template.alias + ".cshtml"); + } else { + vm.template.masterTemplateAlias = null; + setLayout(null); + } + editorService.close(); + }, + close: () => editorService.close() + } + + localizationService.localize("template_mastertemplate").then(title => { + editor.title = title; + + const currentTemplate = vm.templates.find(template => template.alias == vm.template.masterTemplateAlias); + if (currentTemplate) { + editor.currentNode = { + path: currentTemplate.path + }; + } + + editorService.templatePicker(editor); + }); + + } + + function selectMasterTemplate(template) { + + if (template && template.alias) { + vm.template.masterTemplateAlias = template.alias; + setLayout(template.alias + ".cshtml"); + } else { + vm.template.masterTemplateAlias = null; + setLayout(null); + } + + } + + function getMasterTemplateName(masterTemplateAlias, templates) { + if (masterTemplateAlias) { + var templateName = ""; + templates.forEach(function (template) { + if (template.alias === masterTemplateAlias) { + templateName = template.name; + } + }); + return templateName; + } + } + + function removeMasterTemplate() { + + vm.template.masterTemplateAlias = null; + + // call set layout with no paramters to set layout to null + setLayout(); + + } + + function setLayout(templatePath) { + + var templateCode = vm.editor.getValue(); + var newValue = templatePath; + var layoutDefRegex = new RegExp("(@{[\\s\\S]*?Layout\\s*?=\\s*?)(\"[^\"]*?\"|null)(;[\\s\\S]*?})", "gi"); + + if (newValue !== undefined && newValue !== "") { + if (layoutDefRegex.test(templateCode)) { + // Declaration exists, so just update it + templateCode = templateCode.replace(layoutDefRegex, "$1\"" + newValue + "\"$3"); + } else { + // Declaration doesn't exist, so prepend to start of doc + // TODO: Maybe insert at the cursor position, rather than just at the top of the doc? + templateCode = "@{\n\tLayout = \"" + newValue + "\";\n}\n" + templateCode; + } + } else { + if (layoutDefRegex.test(templateCode)) { + // Declaration exists, so just update it + templateCode = templateCode.replace(layoutDefRegex, "$1null$3"); + } + } + + vm.editor.setValue(templateCode); + vm.editor.clearSelection(); + vm.editor.navigateFileStart(); + + vm.editor.focus(); + // set form state to $dirty + setFormState("dirty"); + + } + + + function insert(str) { + vm.editor.focus(); + vm.editor.moveCursorToPosition(vm.currentPosition); + vm.editor.insert(str); + + // set form state to $dirty + setFormState("dirty"); + } + + function wrap(str) { + + var selectedContent = vm.editor.session.getTextRange(vm.editor.getSelectionRange()); + str = str.replace("{0}", selectedContent); + vm.editor.insert(str); + vm.editor.focus(); + + // set form state to $dirty + setFormState("dirty"); + } + + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + + function changeAceEditor() { + setFormState("dirty"); + } + + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if (state === "dirty") { + currentForm.$setDirty(); + } else if (state === "pristine") { + currentForm.$setPristine(); + } + } + + function closeShortcuts() { + vm.showKeyboardShortcut = false; + } + + function submit() { + if ($scope.model.submit) { + $scope.model.template = vm.template; + $scope.model.submit($scope.model); + } + } + + function close() { + if ($scope.model.close) { + $scope.model.close(); + } + } + + vm.init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Templates.EditController", TemplatesEditController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index f909bc197f..a5527095d7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -1,144 +1,142 @@
- + -
+ - + - - + + - - - + + -
+ +
+ Content is not editable when using runtime mode Production. +
-
+
-
+
- +
- + -
+ -
+
-
+
- - +
- - + + - - - -
- -
- -
-
- - - - - - - - - - - - - - - - - + - + - +
- +
-
- +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index b4615eaad8..9b872f137f 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -1,4 +1,4 @@ -(function() { +(function() { "use strict"; describe("templates editor controller", @@ -26,6 +26,7 @@ getCursorPosition: function() {}, getValue: function() {}, setValue: function() {}, + setReadOnly: function () { }, focus: function() {}, clearSelection: function() {}, navigateFileStart: function() {}, diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs index 3547182538..aae69e2f61 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -5,10 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; @@ -77,6 +79,9 @@ public class ContentTypeRepositoryTest : UmbracoIntegrationTest var provider = ScopeProvider; using (var scope = provider.CreateScope()) { + var runtimeSettingsMock = new Mock>(); + runtimeSettingsMock.Setup(x => x.CurrentValue).Returns(new RuntimeSettings()); + var templateRepo = new TemplateRepository( (IScopeAccessor)provider, AppCaches.Disabled, @@ -84,7 +89,8 @@ public class ContentTypeRepositoryTest : UmbracoIntegrationTest FileSystems, IOHelper, ShortStringHelper, - Mock.Of()); + Mock.Of(), + runtimeSettingsMock.Object); var repository = ContentTypeRepository; Template[] templates = { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index bcdcf7666a..9870926544 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -111,7 +111,10 @@ public class DocumentRepositoryTest : UmbracoIntegrationTest { appCaches ??= AppCaches; - templateRepository = new TemplateRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, Mock.Of()); + var runtimeSettingsMock = new Mock>(); + runtimeSettingsMock.Setup(x => x.CurrentValue).Returns(new RuntimeSettings()); + + templateRepository = new TemplateRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, Mock.Of(), runtimeSettingsMock.Object); var tagRepository = new TagRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger()); var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches, ShortStringHelper); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index bcf0f185d1..e0665aaf6d 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; @@ -56,10 +57,13 @@ public class TemplateRepositoryTest : UmbracoIntegrationTest private IHostingEnvironment HostingEnvironment => GetRequiredService(); private FileSystems FileSystems => GetRequiredService(); + private IViewHelper ViewHelper => GetRequiredService(); + private IOptionsMonitor RuntimeSettings => GetRequiredService>(); + private ITemplateRepository CreateRepository(IScopeProvider provider) => - new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, ViewHelper); + new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper, ViewHelper, RuntimeSettings); [Test] public void Can_Instantiate_Repository() diff --git a/version.json b/version.json index c5916db03d..0f9ad08c15 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "10.1.0-alpha.1", + "version": "10.2.0", "assemblyVersion": { "precision": "Build" // optional. Use when you want a more precise assembly version than the default major.minor. },