From 64e7933d57bbd17c40138623a1a0b96de3a36160 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:21:55 +0100 Subject: [PATCH] V11: Merge dev to contrib (#13363) * Bump version * Add IContextCache to deploy connectors (#13287) * Add IContextCache and implementations * Update connector interfaces to use IContextCache * Minor cleanup * Move DeployContextCache prefix to constant * Move default implementations to obsolete methods * Remove DeployContextCache and DictionaryCache Co-authored-by: Andy Butland * Add IContextCache to deploy connectors (#13287) * Add IContextCache and implementations * Update connector interfaces to use IContextCache * Minor cleanup * Move DeployContextCache prefix to constant * Move default implementations to obsolete methods * Remove DeployContextCache and DictionaryCache Co-authored-by: Andy Butland * Parse lockId as invariant (#13284) Co-authored-by: Zeegaan * Fix Sqlite database locking issue (#13246) * Add locking for creating scope * Lock the repository instead * Add scope in action instead of locking in service * Fix up post-merge Co-authored-by: Zeegaan * Bump version to next minor * Fix for UseExceptionHandler no longer working since v10.3 RC (#13218) * Fix for UseExceptionHandler no longer working since v10.3 RC * Update the management api path to match the new one Co-authored-by: Nikolaj * New backoffice: Cleanup management API routes (#13296) * Rename ModelsBuilderDashboard folder to ModelsBuilder * Fix modelsbuilder paths and related naming * Rename analytics route to telemetry * Fix controller bases - routes and tags * Fix items route * Fix more controllerbase routes * Fix route * Fix OpenApi file * Merging DictionaryItem and Dictionary * Fix TrackedReferences naming * Update OpenApi file * Rename Analytics* related types to Telemetry* * New Backoffice: Return AnalyticsLevelViewModel from Telemetry/ (#13298) * Return TelemetryLevelViewModel instead of TelemetryLevel * Fix schema * Change telemetry/current to telemetry/level (cherry picked from commit f2b8494c669cbbf04b623753abbf1be211973aa9) * Add contants for tree and recycle-bin subpaths (cherry picked from commit 4449f56bc00832ea6d357a3854b454791c80e0e2) Co-authored-by: Mole * Updated Smidge, Npoco and MailKit (#13310) * Updated Smidge, Npoco and MailKit * Added missing command (after breaking interface in npoco) * OpenId Connect authentication for new management API (#13318) * First attempt at OpenIddict * Making headway and more TODOs * Redo current policies for multiple schemas + clean up auth controller * Fix bad merge * Clean up some more test code * Fix spacing * Include AddAuthentication() in OpenIddict addition * A little more clean-up * Move application creation to its own implementation + prepare for middleware to handle valid callback URL * Enable refresh token flow * Fix bad merge from v11/dev * Support auth for Swagger and Postman in non-production environments + use default login screen for back-office logins * Add workaround to client side login handling so the OAuth return URL is not corrupted before redirection * Add temporary configuration handling for new backoffice * Restructure the code somewhat, move singular responsibility from management API project * Add recurring task for cleaning up old tokens in the DB * Fix bad merge + make auth controller align with the new management API structure * Explicitly handle the new management API path as a backoffice path (NOTE: this is potentially behaviorally breaking!) * Redo handle the new management API requests as backoffice requests, this time in a non-breaking way * Add/update TODOs * Revert duplication of current auth policies for OpenIddict (as it breaks everything for V11 without the new management APIs) and introduce a dedicated PoC policy setup for OpenIddict. * Fix failing unit tests * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Core/Routing/UmbracoRequestPaths.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * V11: Using IFileProvider to access assets added from packages (#13141) * Creating a FileProviderFactory for getting the package.manifest and grid.editors.config.js files through a file provider * Collecting the package.manifest-s from different sources * Searching different sources for grid.editors.config.js * Using an IFileProvider to collect all tours * Refactoring IconService.cs * Typo * Optimizations when looping through the file system * Moving WebRootFileProviderFactory to Umbraco.Web.Common proj * Removes double registering * pluginLangFileSources includes the localPluginFileSources * Comments * Remove linq from foreach * Change workflow for grid.editors.config.js so we check first physical file, then RCL, then Embedded * Clean up * Check if config dir exists * Discover nested package.manifest files * Fix IFileInfo.PhysicalPath check * Revert 712810e1fd995720047832ee689f804185ea69d6 as that way files in content root are preferred over those in web root * Adding comments * Refactoring * Remove PhysicalPath check * Fix registration of WebRootFileProviderFactory * Use Swashbuckle instead of NSwag (#13350) * First attempt at OpenIddict * Making headway and more TODOs * Redo current policies for multiple schemas + clean up auth controller * Fix bad merge * Clean up some more test code * Fix spacing * Include AddAuthentication() in OpenIddict addition * A little more clean-up * Move application creation to its own implementation + prepare for middleware to handle valid callback URL * Enable refresh token flow * Fix bad merge from v11/dev * Support auth for Swagger and Postman in non-production environments + use default login screen for back-office logins * Add workaround to client side login handling so the OAuth return URL is not corrupted before redirection * Add temporary configuration handling for new backoffice * Restructure the code somewhat, move singular responsibility from management API project * Add recurring task for cleaning up old tokens in the DB * Fix bad merge + make auth controller align with the new management API structure * Explicitly handle the new management API path as a backoffice path (NOTE: this is potentially behaviorally breaking!) * Redo handle the new management API requests as backoffice requests, this time in a non-breaking way * Add/update TODOs * Replace NSwag with Swashbuckle and clean up unnecessary client secret workaround * Revert duplication of current auth policies for OpenIddict (as it breaks everything for V11 without the new management APIs) and introduce a dedicated PoC policy setup for OpenIddict. * Fix failing unit tests * A little niceness + export new OpenApi.json and fix path in contract unit test * Redo after merge with v11/dev + filter out unwanted mime types * Remove CreatedResult and NotFoundObjectResult where possible * Custom schema IDs - no more "ViewModel" postfix and make generic lists look less clunky too * A little more explanation for generic schema ID generation * Force Swashbuckle to use enum string names * Update OpenApi.json to match new enum string values * Add clarifying comment about weird looking construct * add workflow to schema (#13349) * add workflow to schema * add licenses to CMSDefinition - intentionally only adding to schema, not registered as options Co-authored-by: Nikolaj Co-authored-by: Ronald Barendse Co-authored-by: Andy Butland Co-authored-by: Zeegaan Co-authored-by: Justin Neville <67802060+justin-nevitech@users.noreply.github.com> Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Co-authored-by: Bjarke Berg Co-authored-by: Kenn Jacobsen Co-authored-by: Nathan Woulfe --- src/JsonSchema/AppSettings.cs | 9 +- src/JsonSchema/appsettings-schema.json | 11 + .../Analytics/AllAnalyticsController.cs | 22 - .../Analytics/AnalyticsControllerBase.cs | 13 - .../Analytics/GetAnalyticsController.cs | 18 - .../Culture/CultureControllerBase.cs | 3 +- .../Tree/DataTypeTreeControllerBase.cs | 5 +- .../Tree/ItemsDataTypeTreeController.cs | 2 +- .../Dictionary/DictionaryControllerBase.cs | 3 +- .../Dictionary/ExportDictionaryController.cs | 4 +- .../Tree/ChildrenDictionaryTreeController.cs} | 6 +- .../Tree/DictionaryTreeControllerBase.cs} | 13 +- .../Tree/ItemsDictionaryTreeController.cs} | 8 +- .../Tree/RootDictionaryTreeController.cs} | 6 +- .../DocumentRecycleBinControllerBase.cs | 5 +- .../Tree/DocumentTreeControllerBase.cs | 5 +- .../Tree/ItemsDocumentTreeController.cs | 2 +- .../DocumentBlueprintTreeControllerBase.cs | 5 +- .../ItemsDocumentBlueprintTreeController.cs | 2 +- .../Tree/DocumentTypeTreeControllerBase.cs | 5 +- .../Tree/ItemsDocumentTypeTreeController.cs | 2 +- .../Controllers/Help/HelpControllerBase.cs | 3 +- .../Install/InstallControllerBase.cs | 3 +- .../Language/LanguageControllerBase.cs | 3 +- .../MediaRecycleBinControllerBase.cs | 5 +- .../Media/Tree/ItemsMediaTreeController.cs | 2 +- .../Media/Tree/MediaTreeControllerBase.cs | 8 +- .../Tree/ItemsMediaTypeTreeController.cs | 2 +- .../Tree/MediaTypeTreeControllerBase.cs | 5 +- .../Tree/ItemsMemberGroupTreeController.cs | 2 +- .../Tree/MemberGroupTreeControllerBase.cs | 5 +- .../Tree/ItemsMemberTypeTreeController.cs | 2 +- .../Tree/MemberTypeTreeControllerBase.cs | 5 +- .../BuildModelsBuilderController.cs | 2 +- .../GetModelsBuilderController.cs | 9 +- .../ModelsBuilderControllerBase.cs | 5 +- .../StatusModelsBuilderController.cs | 2 +- .../Tree/ItemsPartialViewTreeController.cs | 2 +- .../Tree/PartialViewTreeControllerBase.cs | 5 +- .../Profiling/ProfilingControllerBase.cs | 3 +- .../PublishedCacheControllerBase.cs | 3 +- .../Relation/ByChildRelationController.cs | 2 +- .../Relation/RelationControllerBase.cs | 3 +- .../Tree/ItemsRelationTypeTreeController.cs | 2 +- .../Tree/RelationTypeTreeControllerBase.cs | 5 +- .../Script/Tree/ItemsScriptTreeController.cs | 2 +- .../Script/Tree/ScriptTreeControllerBase.cs | 5 +- .../Search/SearchControllerBase.cs | 3 +- .../Security/BackOfficeController.cs | 76 + .../Controllers/Security/Paths.cs | 12 + .../Server/ServerControllerBase.cs | 3 +- .../Tree/ItemsStaticFileTreeController.cs | 2 +- .../Tree/StaticFileTreeControllerBase.cs | 5 +- .../Tree/ItemsStylesheetTreeController.cs | 2 +- .../Tree/StylesheetTreeControllerBase.cs | 5 +- .../Telemetry/AllTelemetryController.cs | 23 + .../Telemetry/GetTelemetryController.cs | 18 + .../SetTelemetryController.cs} | 21 +- .../Telemetry/TelemetryControllerBase.cs | 12 + .../Tree/ItemsTemplateTreeController.cs | 2 +- .../Tree/TemplateTreeControllerBase.cs | 5 +- .../ByIdTrackedReferenceController.cs} | 6 +- .../DescendantsTrackedReferenceController.cs} | 6 +- .../ItemsTrackedReferenceController.cs} | 8 +- .../TrackedReferencesControllerBase.cs | 12 + .../TrackedReferencesControllerBase.cs | 13 - .../Upgrade/UpgradeControllerBase.cs | 3 +- .../BackOfficeAuthBuilderExtensions.cs | 145 + .../ServicesBuilderExtensions.cs | 8 + .../ManagementApiComposer.cs | 102 +- ...ceAuthorizationInitializationMiddleware.cs | 62 + src/Umbraco.Cms.ManagementApi/OpenApi.json | 7561 ++++++++++------- .../OpenApi/EnumSchemaFilter.cs | 23 + .../OpenApi/MimeTypeDocumentFilter.cs | 33 + .../OpenApi/SchemaIdGenerator.cs | 26 + .../Security/BackOfficeApplicationManager.cs | 131 + .../Umbraco.Cms.ManagementApi.csproj | 7 +- .../Analytics/AnalyticsLevelViewModel.cs | 10 - .../Telemetry/TelemetryViewModel.cs | 10 + .../Umbraco.Cms.Persistence.SqlServer.csproj | 2 +- .../Configuration/Grid/GridConfig.cs | 25 +- .../Configuration/Grid/GridEditorsConfig.cs | 86 +- .../Configuration/Models/LicensesSettings.cs | 11 + src/Umbraco.Core/Constants-Web.cs | 6 + .../UmbracoBuilder.Configuration.cs | 2 +- src/Umbraco.Core/Deploy/ArtifactBase.cs | 73 +- .../Deploy/ArtifactDeployState.cs | 37 + .../ArtifactDeployStateOfTArtifactTEntity.cs | 38 - src/Umbraco.Core/Deploy/IContextCache.cs | 31 + .../Deploy/IDataTypeConfigurationConnector.cs | 62 +- src/Umbraco.Core/Deploy/IServiceConnector.cs | 93 +- src/Umbraco.Core/Deploy/IValueConnector.cs | 57 +- src/Umbraco.Core/Deploy/PassThroughCache.cs | 34 + .../IGridEditorsConfigFileProviderFactory.cs | 10 + .../IO/IManifestFileProviderFactory.cs | 10 + .../Routing/UmbracoRequestPaths.cs | 18 +- .../Routing/UmbracoRequestPathsOptions.cs | 10 + src/Umbraco.Core/Umbraco.Core.csproj | 4 + .../Deploy/IGridCellValueConnector.cs | 71 +- .../Manifest/ManifestParser.cs | 92 +- .../Umbraco.Infrastructure.csproj | 4 +- .../Constants-OauthClientIds.cs | 23 + .../Configuration/NewBackOfficeSettings.cs | 10 + .../NewBackOfficeSettingsValidator.cs | 25 + .../HostedServices/OpenIddictCleanup.cs | 56 + .../Security/IBackOfficeApplicationManager.cs | 6 + .../Umbraco.New.Cms.Infrastructure.csproj | 4 + .../Controllers/TourController.cs | 187 +- .../UmbracoBuilder.LocalizedText.cs | 26 +- .../Services/IconService.cs | 51 +- .../Trees/MemberGroupTreeController.cs | 3 +- .../UmbracoBuilderExtensions.cs | 8 +- .../WebRootFileProviderFactory.cs | 27 + .../Umbraco.Web.Common.csproj | 4 +- .../src/views/common/login.controller.js | 6 +- .../TestHelpers/TestDatabase.cs | 1 + .../NewBackoffice/OpenAPIContractTest.cs | 6 +- .../Testing/UmbracoIntegrationTest.cs | 5 - .../Objects/TestUmbracoContextFactory.cs | 10 +- .../Umbraco.Core/CoreThings/UdiTests.cs | 4 +- .../Manifest/ManifestParserTests.cs | 3 +- .../Routing/UmbracoRequestPathsTests.cs | 26 +- .../Security/BackOfficeCookieManagerTests.cs | 15 +- version.json | 2 +- 124 files changed, 6358 insertions(+), 3525 deletions(-) delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs => Dictionary/Tree/ChildrenDictionaryTreeController.cs} (82%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs => Dictionary/Tree/DictionaryTreeControllerBase.cs} (77%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs => Dictionary/Tree/ItemsDictionaryTreeController.cs} (72%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/RootDictionaryItemTreeController.cs => Dictionary/Tree/RootDictionaryTreeController.cs} (82%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/BuildModelsBuilderController.cs (96%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/GetModelsBuilderController.cs (73%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/ModelsBuilderControllerBase.cs (65%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/StatusModelsBuilderController.cs (94%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{Analytics/SetAnalyticsController.cs => Telemetry/SetTelemetryController.cs} (51%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/ForItemTrackedReferencesController.cs => TrackedReference/ByIdTrackedReferenceController.cs} (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/DescendantsTrackedReferencesController.cs => TrackedReference/DescendantsTrackedReferenceController.cs} (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/MultipleTrackedReferencesController.cs => TrackedReference/ItemsTrackedReferenceController.cs} (87%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs create mode 100644 src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs create mode 100644 src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs create mode 100644 src/Umbraco.Core/Configuration/Models/LicensesSettings.cs delete mode 100644 src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs create mode 100644 src/Umbraco.Core/Deploy/IContextCache.cs create mode 100644 src/Umbraco.Core/Deploy/PassThroughCache.cs create mode 100644 src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs create mode 100644 src/Umbraco.Core/IO/IManifestFileProviderFactory.cs create mode 100644 src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs create mode 100644 src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs create mode 100644 src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs create mode 100644 src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs create mode 100644 src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs create mode 100644 src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs create mode 100644 src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index a8ea2f5dbba5..f9c7c224beee 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -12,11 +12,12 @@ internal class AppSettings public CmsDefinition? CMS { get; set; } /// - /// Configurations for the Umbraco CMS + /// Configurations for the Umbraco CMS /// public class CmsDefinition { public ContentSettings? Content { get; set; } + public CoreDebugSettings? Debug { get; set; } public ExceptionFilterSettings? ExceptionFilter { get; set; } @@ -37,7 +38,7 @@ public class CmsDefinition public LoggingSettings? Logging { get; set; } - public NuCacheSettings? NuCache { get; set; } + public NuCacheSettings? NuCache { get; set; } public RequestHandlerSettings? RequestHandler { get; set; } @@ -49,7 +50,7 @@ public class CmsDefinition public TypeFinderSettings? TypeFinder { get; set; } - public WebRoutingSettings? WebRouting { get; set; } + public WebRoutingSettings? WebRouting { get; set; } public UmbracoPluginSettings? Plugins { get; set; } @@ -72,5 +73,7 @@ public class CmsDefinition public InstallDefaultDataSettings? DefaultDataCreation { get; set; } public DataTypesSettings? DataTypes { get; set; } + + public LicensesSettings? Licenses { get; set; } } } diff --git a/src/JsonSchema/appsettings-schema.json b/src/JsonSchema/appsettings-schema.json index 95c9a9b4c122..44b006b85340 100644 --- a/src/JsonSchema/appsettings-schema.json +++ b/src/JsonSchema/appsettings-schema.json @@ -41,6 +41,17 @@ "$ref": "appsettings-schema.Umbraco.Deploy.json#/definitions/JsonSchemaDeployDefinition" } ] + }, + "Workflow": { + "description": "Configuration of Umbraco Workflow", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "appsettings-schema.Umbraco.Workflow.json#/definitions/JsonSchemaWorkflowDefinition" + } + ] } } } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs deleted file mode 100644 index 780d6f90d397..000000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.ManagementApi.ViewModels.Pagination; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -public class AllAnalyticsController : AnalyticsControllerBase -{ - [HttpGet("all")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> GetAll(int skip, int take) - { - TelemetryLevel[] levels = Enum.GetValues(); - return await Task.FromResult(new PagedViewModel - { - Total = levels.Length, - Items = levels.Skip(skip).Take(take), - }); - } -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs deleted file mode 100644 index 1ce29ea5c68a..000000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; -using Umbraco.New.Cms.Web.Common.Routing; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -[ApiController] -[VersionedApiBackOfficeRoute("analytics")] -[OpenApiTag("Analytics")] -[ApiVersion("1.0")] -public abstract class AnalyticsControllerBase : ManagementApiControllerBase -{ -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs deleted file mode 100644 index 1c45e41e714c..000000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.ManagementApi.ViewModels.Analytics; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -public class GetAnalyticsController : AnalyticsControllerBase -{ - private readonly IMetricsConsentService _metricsConsentService; - - public GetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; - - [HttpGet] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(AnalyticsLevelViewModel), StatusCodes.Status200OK)] - public async Task Get() => await Task.FromResult(new AnalyticsLevelViewModel { AnalyticsLevel = _metricsConsentService.GetConsentLevel() }); -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs index 7ea0b77ae285..3ab12578244f 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Culture; [ApiController] [VersionedApiBackOfficeRoute("culture")] -[OpenApiTag("Culture")] +[ApiExplorerSettings(GroupName = "Culture")] [ApiVersion("1.0")] public abstract class CultureControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs index 4bdae5a2fa73..e5948770dbfc 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DataType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DataType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DataType}")] +[ApiExplorerSettings(GroupName = "Data Type")] public class DataTypeTreeControllerBase : FolderTreeControllerBase { private readonly IDataTypeService _dataTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs index 82eabcb6f494..34779cca3d68 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs @@ -12,7 +12,7 @@ public ItemsDataTypeTreeController(IEntityService entityService, IDataTypeServic { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs index f102d497ac6c..cfdba7cec139 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary; [ApiController] [VersionedApiBackOfficeRoute("dictionary")] -[OpenApiTag("Dictionary")] +[ApiExplorerSettings(GroupName = "Dictionary")] [ApiVersion("1.0")] // TODO: Add authentication public abstract class DictionaryControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs index f4944220d580..5145db107683 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs @@ -23,13 +23,13 @@ public ExportDictionaryController(ILocalizationService localizationService, IEnt [HttpGet("export/{key:guid}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(NotFoundObjectResult), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)] public async Task ExportDictionary(Guid key, bool includeChildren = false) { IDictionaryItem? dictionaryItem = _localizationService.GetDictionaryItemById(key); if (dictionaryItem is null) { - return await Task.FromResult(NotFound("No dictionary item found with id ")); + return await Task.FromResult(NotFound()); } XElement xml = _entityXmlSerializer.Serialize(dictionaryItem, includeChildren); diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs similarity index 82% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs index 0969b808c9da..8b2e6b23281b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs @@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class ChildrenDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class ChildrenDictionaryTreeController : DictionaryTreeControllerBase { - public ChildrenDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public ChildrenDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs similarity index 77% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs index 0d6a513e2d11..495978980987 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -7,17 +6,17 @@ using Umbraco.Cms.ManagementApi.ViewModels.Tree; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DictionaryItem}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DictionaryItem))] -// NOTE: at the moment dictionary items aren't supported by EntityService, so we have little use of the +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/dictionary")] +[ApiExplorerSettings(GroupName = "Dictionary")] +// NOTE: at the moment dictionary items (renamed to dictionary tree) aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. -public class DictionaryItemTreeControllerBase : EntityTreeControllerBase +public class DictionaryTreeControllerBase : EntityTreeControllerBase { - public DictionaryItemTreeControllerBase(IEntityService entityService, ILocalizationService localizationService) + public DictionaryTreeControllerBase(IEntityService entityService, ILocalizationService localizationService) : base(entityService) => LocalizationService = localizationService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs similarity index 72% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs index 69d2fda33d4d..ea2de2058afc 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs @@ -4,16 +4,16 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class ItemsDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class ItemsDictionaryTreeController : DictionaryTreeControllerBase { - public ItemsDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public ItemsDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs similarity index 82% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs index 18abf7a72860..65981cb5189c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs @@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class RootDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class RootDictionaryTreeController : DictionaryTreeControllerBase { - public RootDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public RootDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs index 747b6b3296dc..2edcb07a396d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -14,10 +13,10 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/recycle-bin")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Document}")] [RequireDocumentTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] -[OpenApiTag(nameof(Constants.UdiEntityType.Document))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))] public class DocumentRecycleBinControllerBase : RecycleBinControllerBase { public DocumentRecycleBinControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs index a20237537d18..b838c0379094 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; @@ -16,8 +15,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Document))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Document}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))] public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBase { private readonly IPublicAccessService _publicAccessService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs index a18dfea069a5..a9dd8326d6a4 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs @@ -21,7 +21,7 @@ public ItemsDocumentTreeController( { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null, string? culture = null) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs index c6247da3a9b0..ad4e32cd870a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentBlueprint}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DocumentBlueprint))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentBlueprint}")] +[ApiExplorerSettings(GroupName = "Document Blueprint")] public class DocumentBlueprintTreeControllerBase : EntityTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs index 6b7edb6fab1d..e077d7eb4970 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs @@ -13,7 +13,7 @@ public ItemsDocumentBlueprintTreeController(IEntityService entityService, IConte { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs index cc4c224ef575..91676b9f808a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DocumentType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentType}")] +[ApiExplorerSettings(GroupName = "Document Type")] public class DocumentTypeTreeControllerBase : FolderTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs index e19bf249c6d8..28da775a585d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs @@ -12,7 +12,7 @@ public ItemsDocumentTypeTreeController(IEntityService entityService, IContentTyp { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs index 7220b8738baa..9a50d45186a1 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Help; [ApiController] [VersionedApiBackOfficeRoute("help")] -[OpenApiTag("Help")] +[ApiExplorerSettings(GroupName = "Help")] [ApiVersion("1.0")] public abstract class HelpControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs index 359e62b4b82d..04cd4885bb2d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.ManagementApi.Filters; using Umbraco.New.Cms.Web.Common.Routing; @@ -8,7 +7,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Install; [ApiController] [VersionedApiBackOfficeRoute("install")] -[OpenApiTag("Install")] +[ApiExplorerSettings(GroupName = "Install")] [RequireRuntimeLevel(RuntimeLevel.Install)] public abstract class InstallControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs index b84088906c87..c316486fede3 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Language; [ApiController] [VersionedApiBackOfficeRoute("language")] -[OpenApiTag("Language")] +[ApiExplorerSettings(GroupName = "Language")] [ApiVersion("1.0")] public abstract class LanguageControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs index 157e1099de60..787a12f95719 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -14,10 +13,10 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/recycle-bin")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Media}")] [RequireMediaTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] -[OpenApiTag(nameof(Constants.UdiEntityType.Media))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))] public class MediaRecycleBinControllerBase : RecycleBinControllerBase { public MediaRecycleBinControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs index 2ebf1a559f3f..cc6074d49bd2 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs @@ -20,7 +20,7 @@ public ItemsMediaTreeController( { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs index c03f05c71d90..67e90efe8e59 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; @@ -15,8 +15,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Media))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Media}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))] public class MediaTreeControllerBase : UserStartNodeTreeControllerBase { private readonly AppCaches _appCaches; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs index 363751fee7f7..309711cd7dc6 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs @@ -12,7 +12,7 @@ public ItemsMediaTypeTreeController(IEntityService entityService, IMediaTypeServ { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs index 5b06c46439a2..d47691df4e71 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MediaType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MediaType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MediaType}")] +[ApiExplorerSettings(GroupName = "Media Type")] public class MediaTypeTreeControllerBase : FolderTreeControllerBase { private readonly IMediaTypeService _mediaTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs index 94db46be4ed4..d3be627d306b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs @@ -12,7 +12,7 @@ public ItemsMemberGroupTreeController(IEntityService entityService) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs index b3ee033a25c8..455fcf64f799 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberGroup}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MemberGroup))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberGroup}")] +[ApiExplorerSettings(GroupName = "Member Group")] public class MemberGroupTreeControllerBase : EntityTreeControllerBase { public MemberGroupTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs index 249cdd5d67cd..e70ca8f0ee14 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs @@ -12,7 +12,7 @@ public ItemsMemberTypeTreeController(IEntityService entityService) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs index 88183dfd58ee..2924ad3ba190 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MemberType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberType}")] +[ApiExplorerSettings(GroupName = "Member Type")] public class MemberTypeTreeControllerBase : EntityTreeControllerBase { public MemberTypeTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs similarity index 96% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs index c219a5128ef2..7fcf5b58887b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs @@ -8,7 +8,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Extensions; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class BuildModelsBuilderController : ModelsBuilderControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs similarity index 73% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs index aa993cd1f44d..b2080ee61048 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs @@ -1,10 +1,9 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.ManagementApi.Factories; using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class GetModelsBuilderController : ModelsBuilderControllerBase { @@ -12,8 +11,8 @@ public class GetModelsBuilderController : ModelsBuilderControllerBase public GetModelsBuilderController(IModelsBuilderViewModelFactory modelsBuilderViewModelFactory) => _modelsBuilderViewModelFactory = modelsBuilderViewModelFactory; - [HttpGet] - [ProducesResponseType(typeof(CreatedResult), StatusCodes.Status200OK)] + [HttpGet("dashboard")] + [ProducesResponseType(typeof(ModelsBuilderViewModel), StatusCodes.Status200OK)] [MapToApiVersion("1.0")] public async Task> GetDashboard() => await Task.FromResult(Ok(_modelsBuilderViewModelFactory.Create())); } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs similarity index 65% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs index 4871d3b9d63b..aa428d6005e2 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; [ApiController] [VersionedApiBackOfficeRoute("models-builder")] -[OpenApiTag("ModelsBuilder")] +[ApiExplorerSettings(GroupName = "Models Builder")] [ApiVersion("1.0")] public class ModelsBuilderControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs similarity index 94% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs index 9b2cb22e0a87..1f130ffb5c8d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class StatusModelsBuilderController : ModelsBuilderControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs index d6107a844a7f..5a1567028caf 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs @@ -12,7 +12,7 @@ public ItemsPartialViewTreeController(FileSystems fileSystems) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs index 95ad0eb6cfe6..ba2420e85156 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.PartialView}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.PartialView))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.PartialView}")] +[ApiExplorerSettings(GroupName = "Partial View")] public class PartialViewTreeControllerBase : FileSystemTreeControllerBase { public PartialViewTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs index c081f74fe0aa..f1919b250182 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Profiling; @@ -7,7 +6,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Profiling; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("profiling")] -[OpenApiTag("Profiling")] +[ApiExplorerSettings(GroupName = "Profiling")] public class ProfilingControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs index 78dcd539f4f0..914775a563d5 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; @@ -7,7 +6,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("published-cache")] -[OpenApiTag("PublishedCache")] +[ApiExplorerSettings(GroupName = "Published Cache")] public class PublishedCacheControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs index 4639fa7bea02..8d1e88c806d3 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs @@ -22,7 +22,7 @@ public ByChildRelationController( _relationViewModelFactory = relationViewModelFactory; } - [HttpGet("child-relations/{childId:int}")] + [HttpGet("child-relation/{childId:int}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task> ByChild(int childId, int skip, int take, string? relationTypeAlias = "") diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs index 7ec26735d19a..65266ef356a7 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Relation; [ApiController] [VersionedApiBackOfficeRoute("relation")] -[OpenApiTag("Relation")] +[ApiExplorerSettings(GroupName = "Relation")] [ApiVersion("1.0")] // TODO: Implement Authentication public abstract class RelationControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs index 7e2054a59422..ca3f6ff7de05 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs @@ -14,7 +14,7 @@ public ItemsRelationTypeTreeController(IEntityService entityService, IRelationSe : base(entityService) => _relationService = relationService; - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs index c90c12468676..c3497e7a1dfd 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -11,8 +10,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.RelationType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.RelationType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.RelationType}")] +[ApiExplorerSettings(GroupName = "Relation Type")] // NOTE: at the moment relation types aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. public class RelationTypeTreeControllerBase : EntityTreeControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs index 99cd6d990e3e..642fcb63beaa 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs @@ -12,7 +12,7 @@ public ItemsScriptTreeController(FileSystems fileSystems) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs index 4e204da4eedb..f0f73981411f 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Script}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Script))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Script}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Script))] public class ScriptTreeControllerBase : FileSystemTreeControllerBase { public ScriptTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs index 2ab98aa2ff54..9d3befc0227c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Search; [ApiController] [VersionedApiBackOfficeRoute("search")] -[OpenApiTag("Search")] +[ApiExplorerSettings(GroupName = "Search")] public class SearchControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs new file mode 100644 index 000000000000..74ce67ed032a --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs @@ -0,0 +1,76 @@ +using System.Security.Claims; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.BackOffice.Security; +using Umbraco.Extensions; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.Security; + +[ApiController] +[VersionedApiBackOfficeRoute(Paths.BackOfficeApiEndpointTemplate)] +[ApiExplorerSettings(GroupName = "Security")] +public class BackOfficeController : ManagementApiControllerBase +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IBackOfficeSignInManager _backOfficeSignInManager; + private readonly IBackOfficeUserManager _backOfficeUserManager; + + public BackOfficeController(IHttpContextAccessor httpContextAccessor, IBackOfficeSignInManager backOfficeSignInManager, IBackOfficeUserManager backOfficeUserManager) + { + _httpContextAccessor = httpContextAccessor; + _backOfficeSignInManager = backOfficeSignInManager; + _backOfficeUserManager = backOfficeUserManager; + } + + [HttpGet("authorize")] + [HttpPost("authorize")] + [MapToApiVersion("1.0")] + public async Task Authorize() + { + HttpContext context = _httpContextAccessor.GetRequiredHttpContext(); + OpenIddictRequest? request = context.GetOpenIddictServerRequest(); + if (request == null) + { + return BadRequest("Unable to obtain OpenID data from the current request"); + } + + // retrieve the user principal stored in the authentication cookie. + AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType); + if (cookieAuthResult.Succeeded && cookieAuthResult.Principal?.Identity?.Name != null) + { + BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByNameAsync(cookieAuthResult.Principal.Identity.Name); + if (backOfficeUser != null) + { + ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser); + backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString()); + + // TODO: it is not optimal to append all claims to the token. + // the token size grows with each claim, although it is still smaller than the old cookie. + // see if we can find a better way so we do not risk leaking sensitive data in bearer tokens. + // maybe work with scopes instead? + Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray(); + foreach (Claim backOfficeClaim in backOfficeClaims) + { + backOfficeClaim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); + } + + if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess)) + { + // "offline_access" scope is required to use refresh tokens + backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess); + } + + return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal); + } + } + + return new ChallengeResult(new[] { Constants.Security.BackOfficeAuthenticationType }); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs new file mode 100644 index 000000000000..3ce1b7c3c6b8 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Cms.ManagementApi.Controllers.Security; + +public static class Paths +{ + public const string BackOfficeApiEndpointTemplate = "security/back-office"; + + public static string BackOfficeApiAuthorizationEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/authorize"); + + public static string BackOfficeApiTokenEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/token"); + + private static string BackOfficeApiEndpointPath(string relativePath) => $"/umbraco/management/api/v1.0/{relativePath}"; +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs index 9dd6b3a1929a..11674dcc2320 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Server; [ApiController] [VersionedApiBackOfficeRoute("server")] -[OpenApiTag("Server")] +[ApiExplorerSettings(GroupName = "Server")] public abstract class ServerControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs index 205f92d94f70..1b8bb4b8b821 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs @@ -12,7 +12,7 @@ public ItemsStaticFileTreeController(IPhysicalFileSystem physicalFileSystem) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs index ec50a544951b..a66898619a46 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.StaticFile.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute("static-file/tree")] -[OpenApiTag("StaticFile")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/static-file")] +[ApiExplorerSettings(GroupName = "Static File")] public class StaticFileTreeControllerBase : FileSystemTreeControllerBase { private static readonly string[] _allowedRootFolders = { "App_Plugins", "wwwroot" }; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs index de2e779ba1f4..12b04a9c08d8 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs @@ -12,7 +12,7 @@ public ItemsStylesheetTreeController(FileSystems fileSystems) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs index b5297522932d..c0fe829806af 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Stylesheet.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Stylesheet}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Stylesheet))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Stylesheet}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Stylesheet))] public class StylesheetTreeControllerBase : FileSystemTreeControllerBase { public StylesheetTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs new file mode 100644 index 000000000000..7e3eed886b94 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.ManagementApi.ViewModels.Pagination; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +public class AllTelemetryController : TelemetryControllerBase +{ + [HttpGet] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task> GetAll(int skip, int take) + { + TelemetryLevel[] levels = Enum.GetValues(); + return await Task.FromResult(new PagedViewModel + { + Total = levels.Length, + Items = levels.Skip(skip).Take(take).Select(level => new TelemetryViewModel { TelemetryLevel = level }), + }); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs new file mode 100644 index 000000000000..3e6323343b45 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +public class GetTelemetryController : TelemetryControllerBase +{ + private readonly IMetricsConsentService _metricsConsentService; + + public GetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; + + [HttpGet("level")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(TelemetryViewModel), StatusCodes.Status200OK)] + public async Task Get() => await Task.FromResult(new TelemetryViewModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs similarity index 51% rename from src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs index cfdd1e8b4ff4..0687997ff0ba 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs @@ -1,36 +1,35 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.ManagementApi.ViewModels.Analytics; -using Umbraco.Cms.ManagementApi.ViewModels.Server; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; -public class SetAnalyticsController : AnalyticsControllerBase +public class SetTelemetryController : TelemetryControllerBase { private readonly IMetricsConsentService _metricsConsentService; - public SetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; + public SetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; - [HttpPost] + [HttpPost("level")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task SetConsentLevel(AnalyticsLevelViewModel analyticsLevelViewModel) + public async Task SetConsentLevel(TelemetryViewModel telemetryViewModel) { - if (!Enum.IsDefined(analyticsLevelViewModel.AnalyticsLevel)) + if (!Enum.IsDefined(telemetryViewModel.TelemetryLevel)) { var invalidModelProblem = new ProblemDetails { - Title = "Invalid AnalyticsLevel value", - Detail = "The provided value for AnalyticsLevel is not valid", + Title = "Invalid TelemetryLevel value", + Detail = "The provided value for TelemetryLevel is not valid", Status = StatusCodes.Status400BadRequest, Type = "Error", }; return BadRequest(invalidModelProblem); } - _metricsConsentService.SetConsentLevel(analyticsLevelViewModel.AnalyticsLevel); + _metricsConsentService.SetConsentLevel(telemetryViewModel.TelemetryLevel); return await Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs new file mode 100644 index 000000000000..ea5835b2ca82 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +[ApiController] +[VersionedApiBackOfficeRoute("telemetry")] +[ApiExplorerSettings(GroupName = "Telemetry")] +[ApiVersion("1.0")] +public abstract class TelemetryControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs index fb4a29c62112..877f740df794 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs @@ -12,7 +12,7 @@ public ItemsTemplateTreeController(IEntityService entityService) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs index be885f26bed8..c27206c66541 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Template.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Template}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Template))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Template}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Template))] public class TemplateTreeControllerBase : EntityTreeControllerBase { public TemplateTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs index a415cf08bfaa..3c1998a7e48c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class ForItemTrackedReferencesController : TrackedReferencesControllerBase +public class ByIdTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesService; private readonly IUmbracoMapper _umbracoMapper; - public ForItemTrackedReferencesController(ITrackedReferencesService trackedReferencesService, IUmbracoMapper umbracoMapper) + public ByIdTrackedReferenceController(ITrackedReferencesService trackedReferencesService, IUmbracoMapper umbracoMapper) { _trackedReferencesService = trackedReferencesService; _umbracoMapper = umbracoMapper; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs index ecac832a8440..20f119d17382 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class DescendantsTrackedReferencesController : TrackedReferencesControllerBase +public class DescendantsTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public DescendantsTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public DescendantsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs similarity index 87% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs index 041d208915b4..39ad9c2560df 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class MultipleTrackedReferencesController : TrackedReferencesControllerBase +public class ItemsTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public MultipleTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public ItemsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; @@ -27,7 +27,7 @@ public MultipleTrackedReferencesController(ITrackedReferencesService trackedRefe /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). /// This is basically finding children of relations. /// - [HttpGet("multiple")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task>> GetPagedReferencedItems([FromQuery]int[] ids, long skip, long take, bool? filterMustBeIsDependency) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs new file mode 100644 index 000000000000..3b62a0fe69a2 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; + +[ApiController] +[VersionedApiBackOfficeRoute("tracked-reference")] +[ApiExplorerSettings(GroupName = "Tracked Reference")] +[ApiVersion("1.0")] +public abstract class TrackedReferenceControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs deleted file mode 100644 index be40c80bf1c1..000000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; -using Umbraco.New.Cms.Web.Common.Routing; - -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; - -[ApiController] -[VersionedApiBackOfficeRoute("tracked-references")] -[OpenApiTag("TrackedReferences")] -[ApiVersion("1.0")] -public abstract class TrackedReferencesControllerBase : ManagementApiControllerBase -{ -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs index 084515aba2e2..6e8e056280ea 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.ManagementApi.Filters; using Umbraco.New.Cms.Web.Common.Routing; @@ -11,7 +10,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Upgrade; [ApiController] [RequireRuntimeLevel(RuntimeLevel.Upgrade)] [VersionedApiBackOfficeRoute("upgrade")] -[OpenApiTag("Upgrade")] +[ApiExplorerSettings(GroupName = "Upgrade")] public abstract class UpgradeControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs new file mode 100644 index 000000000000..5331acaaa9f8 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs @@ -0,0 +1,145 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenIddict.Validation.AspNetCore; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.ManagementApi.Middleware; +using Umbraco.Cms.ManagementApi.Security; +using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.New.Cms.Infrastructure.HostedServices; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.DependencyInjection; + +public static class BackOfficeAuthBuilderExtensions +{ + public static IUmbracoBuilder AddBackOfficeAuthentication(this IUmbracoBuilder builder) + { + builder + .AddDbContext() + .AddOpenIddict(); + + return builder; + } + + private static IUmbracoBuilder AddDbContext(this IUmbracoBuilder builder) + { + builder.Services.AddDbContext(options => + { + // Configure the DB context + // TODO: use actual Umbraco DbContext once EF is implemented - and remove dependency on Microsoft.EntityFrameworkCore.InMemory + options.UseInMemoryDatabase(nameof(DbContext)); + + // Register the entity sets needed by OpenIddict. + options.UseOpenIddict(); + }); + + return builder; + } + + private static IUmbracoBuilder AddOpenIddict(this IUmbracoBuilder builder) + { + builder.Services.AddAuthentication(); + builder.Services.AddAuthorization(CreatePolicies); + + builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + options + .UseEntityFrameworkCore() + .UseDbContext(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization and token endpoints. + options + .SetAuthorizationEndpointUris(Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint) + .SetTokenEndpointUris(Controllers.Security.Paths.BackOfficeApiTokenEndpoint); + + // Enable authorization code flow with PKCE + options + .AllowAuthorizationCodeFlow() + .RequireProofKeyForCodeExchange() + .AllowRefreshTokenFlow(); + + // Register the encryption and signing credentials. + // - see https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html + options + // TODO: use actual certificates here, see docs above + .AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate() + .DisableAccessTokenEncryption(); + + // Register the ASP.NET Core host and configure for custom authentication endpoint. + options + .UseAspNetCore() + .EnableAuthorizationEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + + builder.Services.AddHostedService(); + builder.Services.AddHostedService(); + + return builder; + } + + // TODO: remove this once EF is implemented + public class DatabaseManager : IHostedService + { + private readonly IServiceProvider _serviceProvider; + + public DatabaseManager(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + public async Task StartAsync(CancellationToken cancellationToken) + { + using IServiceScope scope = _serviceProvider.CreateScope(); + + DbContext context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(cancellationToken); + + // TODO: add BackOfficeAuthorizationInitializationMiddleware before UseAuthorization (to make it run for unauthorized API requests) and remove this + IBackOfficeApplicationManager backOfficeApplicationManager = scope.ServiceProvider.GetRequiredService(); + await backOfficeApplicationManager.EnsureBackOfficeApplicationAsync(new Uri("https://localhost:44331/"), cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } + + // TODO: move this to an appropriate location and implement the policy scheme that should be used for the new management APIs + private static void CreatePolicies(AuthorizationOptions options) + { + void AddPolicy(string policyName, string claimType, params string[] allowedClaimValues) + { + options.AddPolicy($"New{policyName}", policy => + { + policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + policy.RequireClaim(claimType, allowedClaimValues); + }); + } + + // NOTE: these are ONLY sample policies that allow us to test the new management APIs + AddPolicy(AuthorizationPolicies.SectionAccessContent, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); + AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); + AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); + AddPolicy(AuthorizationPolicies.SectionAccessMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); + AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content, Constants.Applications.Media); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs index cb739478c56e..9a05c17bfe22 100644 --- a/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs @@ -1,7 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.ManagementApi.Serialization; using Umbraco.Cms.ManagementApi.Services; +using Umbraco.Extensions; using Umbraco.New.Cms.Core.Services.Installer; using Umbraco.New.Cms.Core.Services.Languages; @@ -17,6 +19,12 @@ internal static IUmbracoBuilder AddServices(this IUmbracoBuilder builder) builder.Services.AddTransient(); builder.Services.AddTransient(); + // TODO: handle new management API path in core UmbracoRequestPaths (it's a behavioural breaking change so it goes here for now) + builder.Services.Configure(options => + { + options.IsBackOfficeRequest = urlPath => urlPath.InvariantStartsWith($"/umbraco/management/api/"); + }); + return builder; } } diff --git a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs index 071b6de63a67..fa1d34a30a23 100644 --- a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs @@ -3,22 +3,23 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; -using NSwag.AspNetCore; +using Microsoft.OpenApi.Models; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.ManagementApi.Configuration; using Umbraco.Cms.ManagementApi.DependencyInjection; +using Umbraco.Cms.ManagementApi.OpenApi; using Umbraco.Cms.Web.Common.ApplicationBuilder; using Umbraco.Extensions; +using Umbraco.New.Cms.Core; +using Umbraco.New.Cms.Core.Models.Configuration; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.ManagementApi; @@ -26,7 +27,7 @@ namespace Umbraco.Cms.ManagementApi; public class ManagementApiComposer : IComposer { private const string ApiTitle = "Umbraco Backoffice API"; - private const string ApiAllName = "All"; + private const string ApiDefaultDocumentName = "v1"; private ApiVersion DefaultApiVersion => new(1, 0); @@ -44,7 +45,8 @@ public void Compose(IUmbracoBuilder builder) .AddTrees() .AddFactories() .AddServices() - .AddMappers(); + .AddMappers() + .AddBackOfficeAuthentication(); services.AddApiVersioning(options => { @@ -55,16 +57,63 @@ public void Compose(IUmbracoBuilder builder) options.UseApiBehavior = false; }); - services.AddOpenApiDocument(options => + services.AddSwaggerGen(swaggerGenOptions => { - options.Title = ApiTitle; - options.Version = ApiAllName; - options.DocumentName = ApiAllName; - options.Description = "This shows all APIs available in this version of Umbraco - Including all the legacy apis that is available for backward compatibility"; - options.PostProcess = document => + swaggerGenOptions.SwaggerDoc( + ApiDefaultDocumentName, + new OpenApiInfo + { + Title = ApiTitle, + Version = DefaultApiVersion.ToString(), + Description = "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility" + }); + + swaggerGenOptions.DocInclusionPredicate((_, api) => !string.IsNullOrWhiteSpace(api.GroupName)); + + swaggerGenOptions.TagActionsBy(api => new [] { api.GroupName }); + + // see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting + string ActionSortKeySelector(ApiDescription apiDesc) + => $"{apiDesc.GroupName}_{apiDesc.ActionDescriptor.AttributeRouteInfo?.Template ?? apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.ActionDescriptor.RouteValues["action"]}_{apiDesc.HttpMethod}"; + swaggerGenOptions.OrderActionsBy(ActionSortKeySelector); + + swaggerGenOptions.AddSecurityDefinition("OAuth", new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Name = "Umbraco", + Type = SecuritySchemeType.OAuth2, + Description = "Umbraco Authentication", + Flows = new OpenApiOAuthFlows + { + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri(Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint, UriKind.Relative), + TokenUrl = new Uri(Controllers.Security.Paths.BackOfficeApiTokenEndpoint, UriKind.Relative) + } + } + }); + + swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement { - document.Tags = document.Tags.OrderBy(tag => tag.Name).ToList(); - }; + // this weird looking construct works because OpenApiSecurityRequirement + // is a specialization of Dictionary<,> + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "OAuth", + Type = ReferenceType.SecurityScheme + } + }, + new List { } + } + }); + + swaggerGenOptions.DocumentFilter(); + swaggerGenOptions.SchemaFilter(); + + swaggerGenOptions.CustomSchemaIds(SchemaIdGenerator.Generate); }); services.AddVersionedApiExplorer(options => @@ -78,6 +127,10 @@ public void Compose(IUmbracoBuilder builder) services.AddControllers(); builder.Services.ConfigureOptions(); + // TODO: when this is moved to core, make the AddUmbracoOptions extension private again and remove core InternalsVisibleTo for Umbraco.Cms.ManagementApi + builder.AddUmbracoOptions(); + builder.Services.AddSingleton, NewBackOfficeSettingsValidator>(); + builder.Services.Configure(options => { options.AddFilter(new UmbracoPipelineFilter( @@ -126,21 +179,18 @@ public void Compose(IUmbracoBuilder builder) GlobalSettings? settings = provider.GetRequiredService>().Value; IHostingEnvironment hostingEnvironment = provider.GetRequiredService(); var officePath = settings.GetBackOfficePath(hostingEnvironment); - // serve documents (same as app.UseSwagger()) - applicationBuilder.UseOpenApi(config => + + applicationBuilder.UseSwagger(swaggerOptions => { - config.Path = $"{officePath}/swagger/{{documentName}}/swagger.json"; + swaggerOptions.RouteTemplate = $"{officePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json"; }); - - // Serve Swagger UI - applicationBuilder.UseSwaggerUi3(config => + applicationBuilder.UseSwaggerUI(swaggerUiOptions => { - config.Path = officePath + "/swagger"; - config.SwaggerRoutes.Clear(); - var swaggerPath = $"{officePath}/swagger/{ApiAllName}/swagger.json"; - config.SwaggerRoutes.Add(new SwaggerUi3Route(ApiAllName, swaggerPath)); - config.OperationsSorter = "alpha"; - config.TagsSorter = "alpha"; + swaggerUiOptions.SwaggerEndpoint($"{officePath}/swagger/v1/swagger.json", $"{ApiTitle} {DefaultApiVersion}"); + swaggerUiOptions.RoutePrefix = $"{officePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}/swagger"; + + swaggerUiOptions.OAuthClientId(Constants.OauthClientIds.Swagger); + swaggerUiOptions.OAuthUsePkce(); }); } }, diff --git a/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs b/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs new file mode 100644 index 000000000000..6ecebb33623c --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Routing; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.Middleware; + +public class BackOfficeAuthorizationInitializationMiddleware : IMiddleware +{ + private static bool _firstBackOfficeRequest; + private static SemaphoreSlim _firstBackOfficeRequestLocker = new(1); + + private readonly UmbracoRequestPaths _umbracoRequestPaths; + private readonly IServiceProvider _serviceProvider; + + public BackOfficeAuthorizationInitializationMiddleware(UmbracoRequestPaths umbracoRequestPaths, IServiceProvider serviceProvider) + { + _umbracoRequestPaths = umbracoRequestPaths; + _serviceProvider = serviceProvider; + } + + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + await InitializeBackOfficeAuthorizationOnceAsync(context); + await next(context); + } + + private async Task InitializeBackOfficeAuthorizationOnceAsync(HttpContext context) + { + if (_firstBackOfficeRequest) + { + return; + } + + if (_umbracoRequestPaths.IsBackOfficeRequest(context.Request.Path) == false) + { + return; + } + + await _firstBackOfficeRequestLocker.WaitAsync(); + if (_firstBackOfficeRequest == false) + { + using IServiceScope scope = _serviceProvider.CreateScope(); + IBackOfficeApplicationManager backOfficeApplicationManager = scope.ServiceProvider.GetRequiredService(); + await backOfficeApplicationManager.EnsureBackOfficeApplicationAsync(new Uri(context.Request.GetDisplayUrl())); + _firstBackOfficeRequest = true; + } + + _firstBackOfficeRequestLocker.Release(); + } +} + +// TODO: remove this (used for testing BackOfficeAuthorizationInitializationMiddleware until it can be added to the existing UseBackOffice extension) +// public static class UmbracoApplicationBuilderExtensions +// { +// public static IUmbracoApplicationBuilderContext UseNewBackOffice(this IUmbracoApplicationBuilderContext builder) +// { +// builder.AppBuilder.UseMiddleware(); +// return builder; +// } +// } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi.json b/src/Umbraco.Cms.ManagementApi/OpenApi.json index b821f26e2474..c0b9f4ef0b77 100644 --- a/src/Umbraco.Cms.ManagementApi/OpenApi.json +++ b/src/Umbraco.Cms.ManagementApi/OpenApi.json @@ -1,73 +1,41 @@ { - "x-generator": "NSwag v13.17.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))", - "openapi": "3.0.0", + "openapi": "3.0.1", "info": { "title": "Umbraco Backoffice API", - "description": "This shows all APIs available in this version of Umbraco - Including all the legacy apis that is available for backward compatibility", - "version": "All" + "description": "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility", + "version": "1.0" }, - "servers": [ - { - "url": "https://localhost:44331" - } - ], "paths": { - "/umbraco/management/api/v1/upgrade/authorize": { - "post": { + "/umbraco/management/api/v1/culture": { + "get": { "tags": [ - "Upgrade" + "Culture" ], - "operationId": "AuthorizeUpgrade_Authorize", - "responses": { - "200": { - "description": "" - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" } }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" } } - } - } - }, - "/umbraco/management/api/v1/upgrade/settings": { - "get": { - "tags": [ - "Upgrade" ], - "operationId": "SettingsUpgrade_Settings", "responses": { "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpgradeSettingsViewModel" - } - } - } - }, - "428": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/PagedCulture" } } } @@ -75,60 +43,54 @@ } } }, - "/umbraco/management/api/v1/tracked-references/descendants/{parentId}": { + "/umbraco/management/api/v1/tree/data-type/children": { "get": { "tags": [ - "TrackedReferences" + "Data Type" ], - "summary": "Gets a page list of the child nodes of the current item used in any kind of relation.", - "description": "Used when deleting and unpublishing a single item to check if this item has any descending items that are in any\nkind of relation.\nThis is basically finding the descending items which are children in relations.", - "operationId": "DescendantsTrackedReferences_Descendants", "parameters": [ { - "name": "parentId", - "in": "path", - "required": true, + "name": "parentKey", + "in": "query", "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 + "type": "string", + "format": "uuid" + } }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int64" - }, - "x-position": 2 + "format": "int32", + "default": 0 + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int64" - }, - "x-position": 3 + "format": "int32", + "default": 100 + } }, { - "name": "filterMustBeIsDependency", + "name": "foldersOnly", "in": "query", "schema": { "type": "boolean", - "nullable": true - }, - "x-position": 4 + "default": false + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -136,60 +98,34 @@ } } }, - "/umbraco/management/api/v1/tracked-references/{id}": { + "/umbraco/management/api/v1/tree/data-type/item": { "get": { "tags": [ - "TrackedReferences" + "Data Type" ], - "summary": "Gets a page list of tracked references for the current item, so you can see where an item is being used.", - "description": "Used by info tabs on content, media etc. and for the delete and unpublish of single items.\nThis is basically finding parents of relations.", - "operationId": "ForItemTrackedReferences_Get", "parameters": [ { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", + "name": "key", "in": "query", "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + } } } } @@ -197,65 +133,46 @@ } } }, - "/umbraco/management/api/v1/tracked-references/multiple": { + "/umbraco/management/api/v1/tree/data-type/root": { "get": { "tags": [ - "TrackedReferences" + "Data Type" ], - "summary": "Gets a page list of the items used in any kind of relation from selected integer ids.", - "description": "Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view).\nThis is basically finding children of relations.", - "operationId": "MultipleTrackedReferences_GetPagedReferencedItems", "parameters": [ - { - "name": "ids", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "integer", - "format": "int32" - } - }, - "x-position": 1 - }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int64" - }, - "x-position": 2 + "format": "int32", + "default": 0 + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int64" - }, - "x-position": 3 + "format": "int32", + "default": 100 + } }, { - "name": "filterMustBeIsDependency", + "name": "foldersOnly", "in": "query", "schema": { "type": "boolean", - "nullable": true - }, - "x-position": 4 + "default": false + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -263,50 +180,36 @@ } } }, - "/umbraco/management/api/v1/template/tree/children": { + "/umbraco/management/api/v1/dictionary": { "get": { "tags": [ - "Template" + "Dictionary" ], - "operationId": "ChildrenTemplateTree_Children", "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 + "format": "int32" + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 + "format": "int32" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedDictionaryOverview" } } } @@ -314,40 +217,51 @@ } } }, - "/umbraco/management/api/v1/template/tree/items": { - "get": { + "/umbraco/management/api/v1/dictionary/{id}": { + "patch": { "tags": [ - "Template" + "Dictionary" ], - "operationId": "ItemsTemplateTree_Items", "parameters": [ { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, + "name": "id", + "in": "path", + "required": true, "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 + "type": "string", + "format": "uuid" + } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JsonPatch" + } + } + } + } + }, "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } + "$ref": "#/components/schemas/ContentResult" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -355,92 +269,108 @@ } } }, - "/umbraco/management/api/v1/template/tree/root": { + "/umbraco/management/api/v1/dictionary/{key}": { "get": { "tags": [ - "Template" + "Dictionary" ], - "operationId": "RootTemplateTree_Root", "parameters": [ { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", + "name": "key", + "in": "path", + "required": true, "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 + "type": "string", + "format": "uuid" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/Dictionary" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" } } } } } - } - }, - "/umbraco/management/api/v1/stylesheet/tree/children": { - "get": { + }, + "delete": { "tags": [ - "Stylesheet" + "Dictionary" ], - "operationId": "ChildrenStylesheetTree_Children", "parameters": [ { - "name": "path", - "in": "query", + "name": "key", + "in": "path", + "required": true, "schema": { "type": "string", - "nullable": true - }, - "x-position": 1 + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success" }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } } + } + } + }, + "/umbraco/management/api/v1/dictionary/create": { + "post": { + "tags": [ + "Dictionary" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryItem" + } + } + } + }, "responses": { - "200": { - "description": "", + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedResult" + } + } + } + }, + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } @@ -448,39 +378,48 @@ } } }, - "/umbraco/management/api/v1/stylesheet/tree/items": { + "/umbraco/management/api/v1/dictionary/export/{key}": { "get": { "tags": [ - "Stylesheet" + "Dictionary" ], - "operationId": "ItemsStylesheetTree_Items", "parameters": [ { - "name": "path", - "x-originalName": "paths", + "name": "key", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "includeChildren", "in": "query", - "style": "form", - "explode": true, "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 + "type": "boolean", + "default": false + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } + "type": "string", + "format": "binary" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -488,41 +427,77 @@ } } }, - "/umbraco/management/api/v1/stylesheet/tree/root": { - "get": { + "/umbraco/management/api/v1/dictionary/import": { + "post": { "tags": [ - "Stylesheet" + "Dictionary" ], - "operationId": "RootStylesheetTree_Root", "parameters": [ { - "name": "skip", + "name": "file", "in": "query", "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 + "type": "string" + } }, { - "name": "take", + "name": "parentId", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentResult" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } } + } + } + }, + "/umbraco/management/api/v1/dictionary/upload": { + "post": { + "tags": [ + "Dictionary" ], + "requestBody": { + "content": { } + }, "responses": { "200": { - "description": "", + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DictionaryImport" + } + } + } + }, + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } @@ -530,21 +505,19 @@ } } }, - "/umbraco/management/api/v1/static-file/tree/children": { + "/umbraco/management/api/v1/tree/dictionary/children": { "get": { "tags": [ - "StaticFile" + "Dictionary" ], - "operationId": "ChildrenStaticFileTree_Children", "parameters": [ { - "name": "path", + "name": "parentKey", "in": "query", "schema": { "type": "string", - "nullable": true - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -553,8 +526,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -563,17 +535,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -581,38 +552,33 @@ } } }, - "/umbraco/management/api/v1/static-file/tree/items": { + "/umbraco/management/api/v1/tree/dictionary/item": { "get": { "tags": [ - "StaticFile" + "Dictionary" ], - "operationId": "ItemsStaticFileTree_Items", "parameters": [ { - "name": "path", - "x-originalName": "paths", + "name": "key", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { - "type": "string" + "type": "string", + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -621,12 +587,11 @@ } } }, - "/umbraco/management/api/v1/static-file/tree/root": { + "/umbraco/management/api/v1/tree/dictionary/root": { "get": { "tags": [ - "StaticFile" + "Dictionary" ], - "operationId": "RootStaticFileTree_Root", "parameters": [ { "name": "skip", @@ -635,8 +600,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -645,17 +609,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -663,29 +626,34 @@ } } }, - "/umbraco/management/api/v1/server/status": { + "/umbraco/management/api/v1/tree/document-blueprint/item": { "get": { "tags": [ - "Server" + "Document Blueprint" ], - "operationId": "StatusServer_Get", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" } } - }, + } + ], + "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServerStatusViewModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentBlueprintTreeItem" + } } } } @@ -693,29 +661,38 @@ } } }, - "/umbraco/management/api/v1/server/version": { + "/umbraco/management/api/v1/tree/document-blueprint/root": { "get": { "tags": [ - "Server" + "Document Blueprint" ], - "operationId": "VersionServer_Get", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 } }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VersionViewModel" + "$ref": "#/components/schemas/PagedDocumentBlueprintTreeItem" } } } @@ -723,43 +700,54 @@ } } }, - "/umbraco/management/api/v1/search/index/{indexName}": { + "/umbraco/management/api/v1/tree/document-type/children": { "get": { "tags": [ - "Search" + "Document Type" ], - "summary": "Check if the index has been rebuilt", - "description": "This is kind of rudimentary since there's no way we can know that the index has rebuilt, we\nhave a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache", - "operationId": "IndexDetailsSearch_Index", "parameters": [ { - "name": "indexName", - "in": "path", - "required": true, + "name": "parentKey", + "in": "query", "schema": { "type": "string", - "nullable": true - }, - "x-position": 1 + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } } ], "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IndexViewModel" + "$ref": "#/components/schemas/PagedDocumentTypeTreeItem" } } } @@ -767,40 +755,34 @@ } } }, - "/umbraco/management/api/v1/search/index": { + "/umbraco/management/api/v1/tree/document-type/item": { "get": { "tags": [ - "Search" + "Document Type" ], - "summary": "Get the details for indexers", - "operationId": "IndexListSearch_Indexes", "parameters": [ { - "name": "skip", + "name": "key", "in": "query", "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfIndexViewModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeTreeItem" + } } } } @@ -808,43 +790,46 @@ } } }, - "/umbraco/management/api/v1/search/index/{indexName}/rebuild": { - "post": { + "/umbraco/management/api/v1/tree/document-type/root": { + "get": { "tags": [ - "Search" + "Document Type" ], - "summary": "Rebuilds the index", - "operationId": "IndexRebuildSearch_Rebuild", "parameters": [ { - "name": "indexName", - "in": "path", - "required": true, + "name": "skip", + "in": "query", "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } } ], "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/PagedDocumentTypeTreeItem" } } } @@ -852,40 +837,56 @@ } } }, - "/umbraco/management/api/v1/search/searcher": { + "/umbraco/management/api/v1/recycle-bin/document/children": { "get": { "tags": [ - "Search" + "Document" ], - "summary": "Get the details for searchers", - "operationId": "SearcherListSearch_Searchers", "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 1 + "format": "int32", + "default": 0 + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 2 + "format": "int32", + "default": 100 + } } ], "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfSearcherViewModel" + "$ref": "#/components/schemas/PagedRecycleBinItem" } } } @@ -893,68 +894,48 @@ } } }, - "/umbraco/management/api/v1/search/searcher/{searcherName}/search": { + "/umbraco/management/api/v1/recycle-bin/document/root": { "get": { "tags": [ - "Search" + "Document" ], - "operationId": "SearcherSearchSearch_GetSearchResults", "parameters": [ - { - "name": "searcherName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "query", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 2 - }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 3 + "format": "int32", + "default": 0 + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 4 + "format": "int32", + "default": 100 + } } ], "responses": { - "200": { - "description": "", + "401": { + "description": "Unauthorized", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfPagedViewModelOfSearchResultViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } }, - "404": { - "description": "", + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/PagedRecycleBinItem" } } } @@ -962,21 +943,19 @@ } } }, - "/umbraco/management/api/v1/script/tree/children": { + "/umbraco/management/api/v1/tree/document/children": { "get": { "tags": [ - "Script" + "Document" ], - "operationId": "ChildrenScriptTree_Children", "parameters": [ { - "name": "path", + "name": "parentKey", "in": "query", "schema": { "type": "string", - "nullable": true - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -985,8 +964,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -995,17 +973,31 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/PagedDocumentTreeItem" } } } @@ -1013,38 +1005,48 @@ } } }, - "/umbraco/management/api/v1/script/tree/items": { + "/umbraco/management/api/v1/tree/document/item": { "get": { "tags": [ - "Script" + "Document" ], - "operationId": "ItemsScriptTree_Items", "parameters": [ { - "name": "path", - "x-originalName": "paths", + "name": "key", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { - "type": "string" + "type": "string", + "format": "uuid" } - }, - "x-position": 1 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" + "$ref": "#/components/schemas/DocumentTreeItem" } } } @@ -1053,12 +1055,11 @@ } } }, - "/umbraco/management/api/v1/script/tree/root": { + "/umbraco/management/api/v1/tree/document/root": { "get": { "tags": [ - "Script" + "Document" ], - "operationId": "RootScriptTree_Root", "parameters": [ { "name": "skip", @@ -1067,8 +1068,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -1077,17 +1077,31 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/PagedDocumentTreeItem" } } } @@ -1095,22 +1109,25 @@ } } }, - "/umbraco/management/api/v1/relation/child-relations/{childId}": { + "/umbraco/management/api/v1/help": { "get": { "tags": [ - "Relation" + "Help" ], - "operationId": "ByChildRelation_ByChild", "parameters": [ { - "name": "childId", - "in": "path", - "required": true, + "name": "section", + "in": "query", "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 + "type": "string" + } + }, + { + "name": "tree", + "in": "query", + "schema": { + "type": "string" + } }, { "name": "skip", @@ -1118,8 +1135,7 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } }, { "name": "take", @@ -1127,27 +1143,34 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 3 + } }, { - "name": "relationTypeAlias", + "name": "baseUrl", "in": "query", "schema": { "type": "string", - "default": "", - "nullable": true - }, - "x-position": 4 + "default": "https://our.umbraco.com" + } } ], "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationViewModel" + "$ref": "#/components/schemas/PagedHelpPage" } } } @@ -1155,125 +1178,147 @@ } } }, - "/umbraco/management/api/v1/relation/{id}": { + "/umbraco/management/api/v1/install/settings": { "get": { "tags": [ - "Relation" - ], - "operationId": "ByIdRelation_ById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } + "Install" ], "responses": { - "200": { - "description": "", + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RelationViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } }, - "404": { - "description": "", + "428": { + "description": "Client Error", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ProblemDetails" } } } - } - } - } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstallSettings" + } + } + } + } + } + } }, - "/umbraco/management/api/v1/relation-type/tree/items": { - "get": { + "/umbraco/management/api/v1/install/setup": { + "post": { "tags": [ - "RelationType" + "Install" ], - "operationId": "ItemsRelationTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Install" } - }, - "x-position": 1 + } } - ], + }, "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "200": { - "description": "", + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/install/validate-database": { + "post": { + "tags": [ + "Install" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatabaseInstall" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - } + "$ref": "#/components/schemas/ProblemDetails" } } } + }, + "200": { + "description": "Success" } } } }, - "/umbraco/management/api/v1/relation-type/tree/root": { + "/umbraco/management/api/v1/language": { "get": { "tags": [ - "RelationType" + "Language" ], - "operationId": "RootRelationTypeTree_Root", "parameters": [ { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 + "format": "int32" + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 + "format": "int32" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedLanguage" } } } @@ -1281,100 +1326,172 @@ } } }, - "/umbraco/management/api/v1/published-cache/collect": { - "post": { + "/umbraco/management/api/v1/language/{id}": { + "get": { "tags": [ - "PublishedCache" + "Language" ], - "operationId": "CollectPublishedCache_Collect", - "responses": { - "200": { - "description": "" + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } } - } - } - }, - "/umbraco/management/api/v1/published-cache/rebuild": { - "post": { - "tags": [ - "PublishedCache" ], - "operationId": "RebuildPublishedCache_Collect", "responses": { + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + }, "200": { - "description": "" + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } } } - } - }, - "/umbraco/management/api/v1/published-cache/reload": { - "post": { + }, + "delete": { "tags": [ - "PublishedCache" + "Language" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } ], - "operationId": "ReloadPublishedCache_Reload", "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "200": { - "description": "" + "description": "Success" } } } }, - "/umbraco/management/api/v1/published-cache/status": { - "get": { + "/umbraco/management/api/v1/language/create": { + "post": { "tags": [ - "PublishedCache" + "Language" ], - "operationId": "StatusPublishedCache_Status", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + }, "responses": { - "200": { - "description": "", + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/ProblemDetails" } } } + }, + "201": { + "description": "Created" } } } }, - "/umbraco/management/api/v1/profiling/status": { - "get": { + "/umbraco/management/api/v1/language/update": { + "put": { "tags": [ - "Profiling" + "Language" ], - "operationId": "StatusProfiling_Status", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + }, "responses": { - "200": { - "description": "", + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + }, + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfilingStatusViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } + }, + "200": { + "description": "Success" } } } }, - "/umbraco/management/api/v1/partial-view/tree/children": { + "/umbraco/management/api/v1/tree/media-type/children": { "get": { "tags": [ - "PartialView" + "Media Type" ], - "operationId": "ChildrenPartialViewTree_Children", "parameters": [ { - "name": "path", + "name": "parentKey", "in": "query", "schema": { "type": "string", - "nullable": true - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -1383,8 +1500,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -1393,17 +1509,24 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -1411,38 +1534,33 @@ } } }, - "/umbraco/management/api/v1/partial-view/tree/items": { + "/umbraco/management/api/v1/tree/media-type/item": { "get": { "tags": [ - "PartialView" + "Media Type" ], - "operationId": "ItemsPartialViewTree_Items", "parameters": [ { - "name": "path", - "x-originalName": "paths", + "name": "key", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { - "type": "string" + "type": "string", + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -1451,12 +1569,11 @@ } } }, - "/umbraco/management/api/v1/partial-view/tree/root": { + "/umbraco/management/api/v1/tree/media-type/root": { "get": { "tags": [ - "PartialView" + "Media Type" ], - "operationId": "RootPartialViewTree_Root", "parameters": [ { "name": "skip", @@ -1465,8 +1582,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -1475,17 +1591,24 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -1493,126 +1616,20 @@ } } }, - "/umbraco/management/api/v1/models-builder/build": { - "post": { + "/umbraco/management/api/v1/recycle-bin/media/children": { + "get": { "tags": [ - "ModelsBuilder" + "Media" ], - "operationId": "BuildModelsBuilder_BuildModels", - "responses": { - "201": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" } }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder": { - "get": { - "tags": [ - "ModelsBuilder" - ], - "operationId": "GetModelsBuilder_GetDashboard", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder/status": { - "get": { - "tags": [ - "ModelsBuilder" - ], - "operationId": "StatusModelsBuilder_GetModelsOutOfDateStatus", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OutOfDateStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/member-type/tree/items": { - "get": { - "tags": [ - "MemberType" - ], - "operationId": "ItemsMemberTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/member-type/tree/root": { - "get": { - "tags": [ - "MemberType" - ], - "operationId": "RootMemberTypeTree_Root", - "parameters": [ { "name": "skip", "in": "query", @@ -1620,8 +1637,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -1630,58 +1646,26 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } } ], "responses": { - "200": { - "description": "", + "401": { + "description": "Unauthorized", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } - } - } - } - }, - "/umbraco/management/api/v1/member-group/tree/items": { - "get": { - "tags": [ - "MemberGroup" - ], - "operationId": "ItemsMemberGroupTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { + }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } + "$ref": "#/components/schemas/PagedRecycleBinItem" } } } @@ -1689,12 +1673,11 @@ } } }, - "/umbraco/management/api/v1/member-group/tree/root": { + "/umbraco/management/api/v1/recycle-bin/media/root": { "get": { "tags": [ - "MemberGroup" + "Media" ], - "operationId": "RootMemberGroupTree_Root", "parameters": [ { "name": "skip", @@ -1703,8 +1686,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -1713,17 +1695,26 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } } ], "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedRecycleBinItem" } } } @@ -1731,21 +1722,19 @@ } } }, - "/umbraco/management/api/v1/media/tree/children": { + "/umbraco/management/api/v1/tree/media/children": { "get": { "tags": [ "Media" ], - "operationId": "ChildrenMediaTree_Children", "parameters": [ { "name": "parentKey", "in": "query", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -1754,8 +1743,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -1764,27 +1752,24 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } }, { "name": "dataTypeKey", "in": "query", "schema": { "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 4 + "format": "uuid" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfContentTreeItemViewModel" + "$ref": "#/components/schemas/PagedContentTreeItem" } } } @@ -1792,49 +1777,41 @@ } } }, - "/umbraco/management/api/v1/media/tree/items": { + "/umbraco/management/api/v1/tree/media/item": { "get": { "tags": [ "Media" ], - "operationId": "ItemsMediaTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } }, { "name": "dataTypeKey", "in": "query", "schema": { "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 2 + "format": "uuid" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContentTreeItemViewModel" + "$ref": "#/components/schemas/ContentTreeItem" } } } @@ -1843,12 +1820,11 @@ } } }, - "/umbraco/management/api/v1/media/tree/root": { + "/umbraco/management/api/v1/tree/media/root": { "get": { "tags": [ "Media" ], - "operationId": "RootMediaTree_Root", "parameters": [ { "name": "skip", @@ -1857,8 +1833,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -1867,27 +1842,24 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } }, { "name": "dataTypeKey", "in": "query", "schema": { "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 3 + "format": "uuid" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfContentTreeItemViewModel" + "$ref": "#/components/schemas/PagedContentTreeItem" } } } @@ -1895,112 +1867,34 @@ } } }, - "/umbraco/management/api/v1/media/recycle-bin/children": { + "/umbraco/management/api/v1/tree/member-group/item": { "get": { "tags": [ - "Media" + "Member Group" ], - "operationId": "ChildrenMediaRecycleBin_Children", "parameters": [ { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", + "name": "key", "in": "query", "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } + "type": "array", + "items": { + "type": "string", + "format": "uuid" } } } - } - } - }, - "/umbraco/management/api/v1/media/recycle-bin/root": { - "get": { - "tags": [ - "Media" - ], - "operationId": "RootMediaRecycleBin_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } ], "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } } } } @@ -2008,22 +1902,12 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/children": { + "/umbraco/management/api/v1/tree/member-group/root": { "get": { "tags": [ - "MediaType" + "Member Group" ], - "operationId": "ChildrenMediaTypeTree_Children", "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, { "name": "skip", "in": "query", @@ -2031,8 +1915,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -2041,26 +1924,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -2068,39 +1941,33 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/items": { + "/umbraco/management/api/v1/tree/member-type/item": { "get": { "tags": [ - "MediaType" + "Member Type" ], - "operationId": "ItemsMediaTypeTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/EntityTreeItem" } } } @@ -2109,12 +1976,11 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/root": { + "/umbraco/management/api/v1/tree/member-type/root": { "get": { "tags": [ - "MediaType" + "Member Type" ], - "operationId": "RootMediaTypeTree_Root", "parameters": [ { "name": "skip", @@ -2123,8 +1989,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -2133,26 +1998,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -2160,40 +2015,28 @@ } } }, - "/umbraco/management/api/v1/language": { - "get": { + "/umbraco/management/api/v1/models-builder/build": { + "post": { "tags": [ - "Language" - ], - "summary": "1\n Returns all currently configured languages.", - "operationId": "AllLanguage_GetAll", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } + "Models Builder" ], "responses": { - "200": { - "description": "", + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedResult" + } + } + } + }, + "428": { + "description": "Client Error", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfLanguageViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } @@ -2201,209 +2044,175 @@ } } }, - "/umbraco/management/api/v1/language/{id}": { + "/umbraco/management/api/v1/models-builder/dashboard": { "get": { "tags": [ - "Language" - ], - "operationId": "ByIdLanguage_ById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } + "Models Builder" ], "responses": { - "404": { - "description": "", + "200": { + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ModelsBuilder" } } } - }, + } + } + } + }, + "/umbraco/management/api/v1/models-builder/status": { + "get": { + "tags": [ + "Models Builder" + ], + "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LanguageViewModel" + "$ref": "#/components/schemas/OutOfDateStatus" } } } } } - }, - "delete": { + } + }, + "/umbraco/management/api/v1/tree/partial-view/children": { + "get": { "tags": [ - "Language" + "Partial View" ], - "summary": "Deletes a language with a given ID", - "operationId": "DeleteLanguage_Delete", "parameters": [ { - "name": "id", - "in": "path", - "required": true, + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 1 + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } } ], "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "404": { - "description": "", + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } - }, - "200": { - "description": "" } } } }, - "/umbraco/management/api/v1/language/create": { - "post": { + "/umbraco/management/api/v1/tree/partial-view/item": { + "get": { "tags": [ - "Language" + "Partial View" ], - "summary": "Creates or saves a language", - "operationId": "CreateLanguage_Create", - "requestBody": { - "x-name": "language", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" } } - }, - "required": true, - "x-position": 1 - }, + } + ], "responses": { - "400": { - "description": "", + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } } } } - }, - "201": { - "description": "" } } } }, - "/umbraco/management/api/v1/language/update": { - "put": { + "/umbraco/management/api/v1/tree/partial-view/root": { + "get": { "tags": [ - "Language" + "Partial View" ], - "summary": "Updates a language", - "operationId": "UpdateLanguage_Update", - "requestBody": { - "x-name": "language", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 } }, - "required": true, - "x-position": 1 - }, - "responses": { - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 } - }, - "400": { - "description": "", + } + ], + "responses": { + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } - }, - "200": { - "description": "" } } } }, - "/umbraco/management/api/v1/install/settings": { + "/umbraco/management/api/v1/profiling/status": { "get": { "tags": [ - "Install" + "Profiling" ], - "operationId": "SettingsInstall_Settings", "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InstallSettingsViewModel" + "$ref": "#/components/schemas/ProfilingStatus" } } } @@ -2411,227 +2220,54 @@ } } }, - "/umbraco/management/api/v1/install/setup": { + "/umbraco/management/api/v1/published-cache/collect": { "post": { "tags": [ - "Install" + "Published Cache" ], - "operationId": "SetupInstall_Setup", - "requestBody": { - "x-name": "installData", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InstallViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "" + "description": "Success" } } } }, - "/umbraco/management/api/v1/install/validate-database": { + "/umbraco/management/api/v1/published-cache/rebuild": { "post": { "tags": [ - "Install" + "Published Cache" ], - "operationId": "ValidateDatabaseInstall_ValidateDatabase", - "requestBody": { - "x-name": "viewModel", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DatabaseInstallViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "" + "description": "Success" } } } }, - "/umbraco/management/api/v1/help": { - "get": { + "/umbraco/management/api/v1/published-cache/reload": { + "post": { "tags": [ - "Help" - ], - "operationId": "GetHelp_Get", - "parameters": [ - { - "name": "section", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "tree", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 4 - }, - { - "name": "baseUrl", - "in": "query", - "schema": { - "type": "string", - "default": "https://our.umbraco.com", - "nullable": true - }, - "x-position": 5 - } + "Published Cache" ], "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfHelpPageViewModel" - } - } - } + "description": "Success" } } } }, - "/umbraco/management/api/v1/document/tree/children": { + "/umbraco/management/api/v1/published-cache/status": { "get": { "tags": [ - "Document" - ], - "operationId": "ChildrenDocumentTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 4 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 5 - } + "Published Cache" ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTreeItemViewModel" + "type": "string" } } } @@ -2639,58 +2275,33 @@ } } }, - "/umbraco/management/api/v1/document/tree/items": { + "/umbraco/management/api/v1/tree/relation-type/item": { "get": { "tags": [ - "Document" + "Relation Type" ], - "operationId": "ItemsDocumentTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DocumentTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -2699,12 +2310,11 @@ } } }, - "/umbraco/management/api/v1/document/tree/root": { + "/umbraco/management/api/v1/tree/relation-type/root": { "get": { "tags": [ - "Document" + "Relation Type" ], - "operationId": "RootDocumentTree_Root", "parameters": [ { "name": "skip", @@ -2713,8 +2323,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -2723,36 +2332,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 3 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -2760,60 +2349,39 @@ } } }, - "/umbraco/management/api/v1/document/recycle-bin/children": { + "/umbraco/management/api/v1/relation/{id}": { "get": { "tags": [ - "Document" + "Relation" ], - "operationId": "ChildrenDocumentRecycleBin_Children", "parameters": [ { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", + "name": "id", + "in": "path", + "required": true, "schema": { "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 + "format": "int32" + } } ], "responses": { - "401": { - "description": "", + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/Relation" } } } }, - "200": { - "description": "", + "404": { + "description": "Not Found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -2821,51 +2389,53 @@ } } }, - "/umbraco/management/api/v1/document/recycle-bin/root": { + "/umbraco/management/api/v1/relation/child-relation/{childId}": { "get": { "tags": [ - "Document" + "Relation" ], - "operationId": "RootDocumentRecycleBin_Root", "parameters": [ + { + "name": "childId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 + "format": "int32" + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 + "format": "int32" + } + }, + { + "name": "relationTypeAlias", + "in": "query", + "schema": { + "type": "string", + "default": "" + } } ], "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" + "$ref": "#/components/schemas/PagedRelation" } } } @@ -2873,21 +2443,18 @@ } } }, - "/umbraco/management/api/v1/document-type/tree/children": { + "/umbraco/management/api/v1/tree/script/children": { "get": { "tags": [ - "DocumentType" + "Script" ], - "operationId": "ChildrenDocumentTypeTree_Children", "parameters": [ { - "name": "parentKey", + "name": "path", "in": "query", "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 + "type": "string" + } }, { "name": "skip", @@ -2896,8 +2463,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -2906,26 +2472,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTypeTreeItemViewModel" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } @@ -2933,39 +2489,32 @@ } } }, - "/umbraco/management/api/v1/document-type/tree/items": { + "/umbraco/management/api/v1/tree/script/item": { "get": { "tags": [ - "DocumentType" + "Script" ], - "operationId": "ItemsDocumentTypeTree_Items", "parameters": [ { - "name": "key", - "x-originalName": "keys", + "name": "path", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { - "type": "string", - "format": "guid" + "type": "string" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DocumentTypeTreeItemViewModel" + "$ref": "#/components/schemas/FileSystemTreeItem" } } } @@ -2974,12 +2523,11 @@ } } }, - "/umbraco/management/api/v1/document-type/tree/root": { + "/umbraco/management/api/v1/tree/script/root": { "get": { "tags": [ - "DocumentType" + "Script" ], - "operationId": "RootDocumentTypeTree_Root", "parameters": [ { "name": "skip", @@ -2988,8 +2536,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -2998,26 +2545,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTypeTreeItemViewModel" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } @@ -3025,40 +2562,36 @@ } } }, - "/umbraco/management/api/v1/document-blueprint/tree/items": { + "/umbraco/management/api/v1/search/index": { "get": { "tags": [ - "DocumentBlueprint" + "Search" ], - "operationId": "ItemsDocumentBlueprintTree_Items", "parameters": [ { - "name": "key", - "x-originalName": "keys", + "name": "skip", "in": "query", - "style": "form", - "explode": true, "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentBlueprintTreeItemViewModel" - } + "$ref": "#/components/schemas/PagedIndex" } } } @@ -3066,41 +2599,77 @@ } } }, - "/umbraco/management/api/v1/document-blueprint/tree/root": { + "/umbraco/management/api/v1/search/index/{indexName}": { "get": { "tags": [ - "DocumentBlueprint" + "Search" ], - "operationId": "RootDocumentBlueprintTree_Root", "parameters": [ { - "name": "skip", - "in": "query", + "name": "indexName", + "in": "path", + "required": true, "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Index" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index/{indexName}/rebuild": { + "post": { + "tags": [ + "Search" + ], + "parameters": [ { - "name": "take", - "in": "query", + "name": "indexName", + "in": "path", + "required": true, "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 + "type": "string" + } } ], "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentBlueprintTreeItemViewModel" + "$ref": "#/components/schemas/OkResult" } } } @@ -3108,13 +2677,11 @@ } } }, - "/umbraco/management/api/v1/dictionary": { + "/umbraco/management/api/v1/search/searcher": { "get": { "tags": [ - "Dictionary" + "Search" ], - "summary": "Retrieves a list with all dictionary items", - "operationId": "AllDictionary_All", "parameters": [ { "name": "skip", @@ -3122,8 +2689,7 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 + } }, { "name": "take", @@ -3131,17 +2697,16 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "The IEnumerable`1.\n ", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDictionaryOverviewViewModel" + "$ref": "#/components/schemas/PagedSearcher" } } } @@ -3149,125 +2714,112 @@ } } }, - "/umbraco/management/api/v1/dictionary/{key}": { + "/umbraco/management/api/v1/search/searcher/{searcherName}/search": { "get": { "tags": [ - "Dictionary" + "Search" ], - "summary": "Gets a dictionary item by guid", - "operationId": "ByIdDictionary_ByKey", "parameters": [ { - "name": "key", + "name": "searcherName", "in": "path", "required": true, - "description": "The id.\n ", "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } } ], "responses": { "200": { - "description": "The DictionaryDisplay. Returns a not found response when dictionary item does not exist\n ", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryViewModel" + "$ref": "#/components/schemas/PagedPaged" } } } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ProblemDetails" } } } } } - }, - "delete": { + } + }, + "/umbraco/management/api/v1/security/back-office/authorize": { + "get": { "tags": [ - "Dictionary" + "Security" ], - "summary": "Deletes a data type with a given ID", - "operationId": "DeleteDictionary_Delete", - "parameters": [ - { - "name": "key", - "in": "path", - "required": true, - "description": "The key of the dictionary item to delete", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 + "responses": { + "200": { + "description": "Success" } + } + }, + "post": { + "tags": [ + "Security" ], "responses": { "200": { - "description": "HttpResponseMessage\n " - }, - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } + "description": "Success" } } } }, - "/umbraco/management/api/v1/dictionary/create": { - "post": { + "/umbraco/management/api/v1/server/status": { + "get": { "tags": [ - "Dictionary" + "Server" ], - "summary": "Creates a new dictionary item", - "operationId": "CreateDictionary_Create", - "requestBody": { - "x-name": "dictionaryViewModel", - "description": "The viewmodel to pass to the action", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DictionaryItemViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, "responses": { - "201": { - "description": "The HttpResponseMessage.\n ", + "400": { + "description": "Bad Request", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ProblemDetails" } } } }, - "400": { - "description": "", + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/ServerStatus" } } } @@ -3275,52 +2827,28 @@ } } }, - "/umbraco/management/api/v1/dictionary/export/{key}": { + "/umbraco/management/api/v1/server/version": { "get": { "tags": [ - "Dictionary" - ], - "operationId": "ExportDictionary_ExportDictionary", - "parameters": [ - { - "name": "key", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "includeChildren", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 2 - } + "Server" ], "responses": { - "200": { - "description": "", + "400": { + "description": "Bad Request", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ProblemDetails" } } } }, - "404": { - "description": "", + "200": { + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/Version" } } } @@ -3328,52 +2856,45 @@ } } }, - "/umbraco/management/api/v1/dictionary/import": { - "post": { + "/umbraco/management/api/v1/tree/static-file/children": { + "get": { "tags": [ - "Dictionary" + "Static File" ], - "operationId": "ImportDictionary_ImportDictionary", "parameters": [ { - "name": "file", + "name": "path", "in": "query", "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 + "type": "string" + } }, { - "name": "parentId", + "name": "skip", "in": "query", "schema": { "type": "integer", "format": "int32", - "nullable": true - }, - "x-position": 2 + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } } ], "responses": { "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } @@ -3381,58 +2902,33 @@ } } }, - "/umbraco/management/api/v1/dictionary/{id}": { - "patch": { + "/umbraco/management/api/v1/tree/static-file/item": { + "get": { "tags": [ - "Dictionary" + "Static File" ], - "operationId": "UpdateDictionary_Update", "parameters": [ { - "name": "id", - "in": "path", - "required": true, + "name": "path", + "in": "query", "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - } - ], - "requestBody": { - "x-name": "updateViewModel", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JsonPatchViewModel" - } + "type": "array", + "items": { + "type": "string" } } - }, - "required": true, - "x-position": 2 - }, + } + ], "responses": { "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } } } } @@ -3440,45 +2936,38 @@ } } }, - "/umbraco/management/api/v1/dictionary/upload": { - "post": { + "/umbraco/management/api/v1/tree/static-file/root": { + "get": { "tags": [ - "Dictionary" + "Static File" ], - "operationId": "UploadDictionary_Upload", - "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary", - "nullable": true - } - } - } + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 } } - }, + ], "responses": { "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DictionaryImportViewModel" - } - } - } - }, - "400": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemDetails" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } @@ -3486,21 +2975,18 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/children": { + "/umbraco/management/api/v1/tree/stylesheet/children": { "get": { "tags": [ - "DictionaryItem" + "Stylesheet" ], - "operationId": "ChildrenDictionaryItemTree_Children", "parameters": [ { - "name": "parentKey", + "name": "path", "in": "query", "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 + "type": "string" + } }, { "name": "skip", @@ -3509,8 +2995,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -3519,17 +3004,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedFileSystemTreeItem" } } } @@ -3537,39 +3021,32 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/items": { + "/umbraco/management/api/v1/tree/stylesheet/item": { "get": { "tags": [ - "DictionaryItem" + "Stylesheet" ], - "operationId": "ItemsDictionaryItemTree_Items", "parameters": [ { - "name": "key", - "x-originalName": "keys", + "name": "path", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { - "type": "string", - "format": "guid" + "type": "string" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/FileSystemTreeItem" } } } @@ -3578,12 +3055,11 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/root": { + "/umbraco/management/api/v1/tree/stylesheet/root": { "get": { "tags": [ - "DictionaryItem" + "Stylesheet" ], - "operationId": "RootDictionaryItemTree_Root", "parameters": [ { "name": "skip", @@ -3592,8 +3068,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -3602,39 +3077,121 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/telemetry": { + "get": { + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedTelemetry" + } + } + } } + } + } + }, + "/umbraco/management/api/v1/telemetry/level": { + "get": { + "tags": [ + "Telemetry" ], "responses": { "200": { - "description": "", + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Telemetry" + } + } + } + } + } + }, + "post": { + "tags": [ + "Telemetry" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Telemetry" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/ProblemDetails" } } } + }, + "200": { + "description": "Success" } } } }, - "/umbraco/management/api/v1/data-type/tree/children": { + "/umbraco/management/api/v1/tree/template/children": { "get": { "tags": [ - "DataType" + "Template" ], - "operationId": "ChildrenDataTypeTree_Children", "parameters": [ { "name": "parentKey", "in": "query", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -3643,8 +3200,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -3653,26 +3209,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -3680,39 +3226,33 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/items": { + "/umbraco/management/api/v1/tree/template/item": { "get": { "tags": [ - "DataType" + "Template" ], - "operationId": "ItemsDataTypeTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/EntityTreeItem" } } } @@ -3721,12 +3261,11 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/root": { + "/umbraco/management/api/v1/tree/template/root": { "get": { "tags": [ - "DataType" + "Template" ], - "operationId": "RootDataTypeTree_Root", "parameters": [ { "name": "skip", @@ -3735,8 +3274,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -3745,26 +3283,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -3772,40 +3300,52 @@ } } }, - "/umbraco/management/api/v1/culture": { + "/umbraco/management/api/v1/tracked-reference/{id}": { "get": { "tags": [ - "Culture" + "Tracked Reference" ], - "summary": "Returns all cultures available for creating languages.", - "operationId": "AllCulture_GetAll", "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 1 + "format": "int64" + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 2 + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfCultureViewModel" + "$ref": "#/components/schemas/PagedRelationItem" } } } @@ -3813,39 +3353,52 @@ } } }, - "/umbraco/management/api/v1/analytics/all": { + "/umbraco/management/api/v1/tracked-reference/descendants/{parentId}": { "get": { "tags": [ - "Analytics" + "Tracked Reference" ], - "operationId": "AllAnalytics_GetAll", "parameters": [ + { + "name": "parentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, { "name": "skip", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 1 + "format": "int64" + } }, { "name": "take", "in": "query", "schema": { "type": "integer", - "format": "int32" - }, - "x-position": 2 + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfTelemetryLevel" + "$ref": "#/components/schemas/PagedRelationItem" } } } @@ -3853,45 +3406,82 @@ } } }, - "/umbraco/management/api/v1/analytics": { + "/umbraco/management/api/v1/tracked-reference/item": { "get": { "tags": [ - "Analytics" + "Tracked Reference" ], - "operationId": "GetAnalytics_Get", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AnalyticsLevelViewModel" - } - } - } - } - } - }, + "parameters": [ + { + "name": "ids", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/upgrade/authorize": { "post": { "tags": [ - "Analytics" + "Upgrade" ], - "operationId": "SetAnalytics_SetConsentLevel", - "requestBody": { - "x-name": "analyticsLevelViewModel", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AnalyticsLevelViewModel" + "responses": { + "200": { + "description": "Success" + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } } } }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", + "500": { + "description": "Server Error", "content": { "application/json": { "schema": { @@ -3899,9 +3489,35 @@ } } } - }, + } + } + } + }, + "/umbraco/management/api/v1/upgrade/settings": { + "get": { + "tags": [ + "Upgrade" + ], + "responses": { "200": { - "description": "" + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpgradeSettings" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } } } } @@ -3909,1446 +3525,3455 @@ }, "components": { "schemas": { - "ProblemDetails": { + "Assembly": { "type": "object", - "additionalProperties": { - "nullable": true - }, "properties": { - "type": { - "type": "string", - "nullable": true + "definedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TypeInfo" + }, + "nullable": true, + "readOnly": true }, - "title": { + "exportedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "codeBase": { "type": "string", - "nullable": true + "nullable": true, + "readOnly": true, + "deprecated": true }, - "status": { - "type": "integer", - "format": "int32", - "nullable": true + "entryPoint": { + "$ref": "#/components/schemas/MethodInfo" }, - "detail": { + "fullName": { "type": "string", - "nullable": true + "nullable": true, + "readOnly": true }, - "instance": { + "imageRuntimeVersion": { "type": "string", - "nullable": true - } - } - }, - "UpgradeSettingsViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "currentState": { - "type": "string" + "nullable": true, + "readOnly": true }, - "newState": { - "type": "string" + "isDynamic": { + "type": "boolean", + "readOnly": true }, - "newVersion": { - "type": "string" + "location": { + "type": "string", + "nullable": true, + "readOnly": true }, - "oldVersion": { - "type": "string" + "reflectionOnly": { + "type": "boolean", + "readOnly": true }, - "reportUrl": { - "type": "string" - } - } - }, - "PagedViewModelOfRelationItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" + "isCollectible": { + "type": "boolean", + "readOnly": true }, - "items": { + "isFullyTrusted": { + "type": "boolean", + "readOnly": true + }, + "customAttributes": { "type": "array", "items": { - "$ref": "#/components/schemas/RelationItemViewModel" - } - } - } - }, - "RelationItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "nodeKey": { - "type": "string", - "format": "guid" + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true }, - "nodeName": { + "escapedCodeBase": { "type": "string", - "nullable": true + "nullable": true, + "readOnly": true, + "deprecated": true }, - "nodeType": { - "type": "string", - "nullable": true + "manifestModule": { + "$ref": "#/components/schemas/Module" }, - "contentTypeIcon": { - "type": "string", - "nullable": true + "modules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Module" + }, + "nullable": true, + "readOnly": true }, - "contentTypeAlias": { - "type": "string", - "nullable": true + "globalAssemblyCache": { + "type": "boolean", + "readOnly": true, + "deprecated": true }, - "contentTypeName": { + "hostContext": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "securityRuleSet": { + "$ref": "#/components/schemas/SecurityRuleSet" + } + }, + "additionalProperties": false + }, + "BackOfficeNotification": { + "type": "object", + "properties": { + "header": { "type": "string", "nullable": true }, - "relationTypeName": { + "message": { "type": "string", "nullable": true }, - "relationTypeIsBidirectional": { - "type": "boolean" - }, - "relationTypeIsDependency": { - "type": "boolean" + "notificationType": { + "$ref": "#/components/schemas/NotificationStyle" } - } + }, + "additionalProperties": false + }, + "CallingConventions": { + "enum": [ + "Standard", + "VarArgs", + "Any", + "HasThis", + "ExplicitThis" + ], + "type": "integer", + "format": "int32" }, - "PagedViewModelOfEntityTreeItemViewModel": { + "ConsentLevel": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - }, - "EntityTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/TreeItemViewModel" + "level": { + "$ref": "#/components/schemas/TelemetryLevel" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "format": "guid" - }, - "isContainer": { - "type": "boolean" - }, - "parentKey": { - "type": "string", - "format": "guid", - "nullable": true - } - } + "description": { + "type": "string", + "nullable": true } - ] + }, + "additionalProperties": false }, - "TreeItemViewModel": { + "ConstructorInfo": { "type": "object", - "additionalProperties": false, "properties": { "name": { - "type": "string" + "type": "string", + "nullable": true, + "readOnly": true }, - "type": { - "type": "string" + "declaringType": { + "$ref": "#/components/schemas/Type" }, - "icon": { - "type": "string" + "reflectedType": { + "$ref": "#/components/schemas/Type" }, - "hasChildren": { - "type": "boolean" - } - } - }, - "PagedViewModelOfFileSystemTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" + "module": { + "$ref": "#/components/schemas/Module" }, - "items": { + "customAttributes": { "type": "array", "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - }, - "FileSystemTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/TreeItemViewModel" + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "path": { - "type": "string" - }, - "isFolder": { - "type": "boolean" - } - } + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" } - ] + }, + "additionalProperties": false }, - "ServerStatusViewModel": { + "ContentApp": { "type": "object", - "additionalProperties": false, "properties": { - "serverStatus": { - "$ref": "#/components/schemas/RuntimeLevel" + "name": { + "type": "string", + "nullable": true + }, + "alias": { + "type": "string", + "nullable": true + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "icon": { + "type": "string", + "nullable": true + }, + "view": { + "type": "string", + "nullable": true + }, + "viewModel": { + "nullable": true + }, + "active": { + "type": "boolean" + }, + "badge": { + "$ref": "#/components/schemas/ContentAppBadge" } - } + }, + "additionalProperties": false }, - "RuntimeLevel": { - "type": "string", - "description": "Describes the levels in which the runtime can run.\n ", - "x-enumNames": [ - "Unknown", - "Boot", - "Install", - "Upgrade", - "Run", - "BootFailed" - ], + "ContentAppBadge": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "format": "int32" + }, + "type": { + "$ref": "#/components/schemas/ContentAppBadgeType" + } + }, + "additionalProperties": false + }, + "ContentAppBadgeType": { "enum": [ - "Unknown", - "Boot", - "Install", - "Upgrade", - "Run", - "BootFailed" - ] + "default", + "warning", + "alert" + ], + "type": "integer", + "format": "int32" }, - "VersionViewModel": { + "ContentResult": { "type": "object", - "additionalProperties": false, "properties": { - "version": { - "type": "string" + "content": { + "type": "string", + "nullable": true + }, + "contentType": { + "type": "string", + "nullable": true + }, + "statusCode": { + "type": "integer", + "format": "int32", + "nullable": true } - } + }, + "additionalProperties": false }, - "IndexViewModel": { + "ContentTreeItem": { "type": "object", - "additionalProperties": false, "properties": { "name": { - "type": "string" + "type": "string", + "nullable": true }, - "healthStatus": { + "type": { "type": "string", "nullable": true }, - "isHealthy": { - "type": "boolean" + "icon": { + "type": "string", + "nullable": true }, - "canRebuild": { + "hasChildren": { "type": "boolean" }, - "searcherName": { - "type": "string" + "key": { + "type": "string", + "format": "uuid" }, - "documentCount": { - "type": "integer", - "format": "int64" + "isContainer": { + "type": "boolean" }, - "fieldCount": { - "type": "integer", - "format": "int32" + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "noAccess": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfIndexViewModel": { + "CreatedResult": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "value": { + "nullable": true }, - "items": { + "formatters": { "type": "array", "items": { - "$ref": "#/components/schemas/IndexViewModel" - } - } - } - }, - "PagedViewModelOfSearcherViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" + "$ref": "#/components/schemas/IOutputFormatter" + }, + "nullable": true }, - "items": { + "contentTypes": { "type": "array", "items": { - "$ref": "#/components/schemas/SearcherViewModel" - } + "type": "string" + }, + "nullable": true + }, + "declaredType": { + "$ref": "#/components/schemas/Type" + }, + "statusCode": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "location": { + "type": "string", + "nullable": true } - } + }, + "additionalProperties": false }, - "SearcherViewModel": { + "Culture": { "type": "object", - "additionalProperties": false, "properties": { "name": { - "type": "string" + "type": "string", + "nullable": true + }, + "englishName": { + "type": "string", + "nullable": true } - } + }, + "additionalProperties": false }, - "PagedViewModelOfPagedViewModelOfSearchResultViewModel": { + "CustomAttributeData": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "attributeType": { + "$ref": "#/components/schemas/Type" }, - "items": { + "constructor": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "constructorArguments": { "type": "array", "items": { - "$ref": "#/components/schemas/PagedViewModelOfSearchResultViewModel" - } - } - } - }, - "PagedViewModelOfSearchResultViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" + "$ref": "#/components/schemas/CustomAttributeTypedArgument" + }, + "nullable": true, + "readOnly": true }, - "items": { + "namedArguments": { "type": "array", "items": { - "$ref": "#/components/schemas/SearchResultViewModel" - } + "$ref": "#/components/schemas/CustomAttributeNamedArgument" + }, + "nullable": true, + "readOnly": true } - } + }, + "additionalProperties": false }, - "SearchResultViewModel": { + "CustomAttributeNamedArgument": { "type": "object", - "additionalProperties": false, "properties": { - "id": { - "type": "string" + "memberInfo": { + "$ref": "#/components/schemas/MemberInfo" }, - "score": { - "type": "number", - "format": "float" + "typedValue": { + "$ref": "#/components/schemas/CustomAttributeTypedArgument" }, - "fieldCount": { - "type": "integer", - "format": "int32" + "memberName": { + "type": "string", + "nullable": true, + "readOnly": true }, - "fields": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FieldViewModel" - } + "isField": { + "type": "boolean", + "readOnly": true } - } + }, + "additionalProperties": false }, - "FieldViewModel": { + "CustomAttributeTypedArgument": { "type": "object", - "additionalProperties": false, "properties": { - "name": { - "type": "string" + "argumentType": { + "$ref": "#/components/schemas/Type" }, - "values": { - "type": "array", - "items": { - "type": "string" - } + "value": { + "nullable": true } - } + }, + "additionalProperties": false }, - "PagedViewModelOfRelationViewModel": { + "DatabaseInstall": { + "required": [ + "id", + "providerName" + ], "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "id": { + "type": "string", + "format": "uuid" }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationViewModel" - } + "providerName": { + "minLength": 1, + "type": "string" + }, + "server": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "username": { + "type": "string", + "nullable": true + }, + "password": { + "type": "string", + "nullable": true + }, + "useIntegratedAuthentication": { + "type": "boolean" + }, + "connectionString": { + "type": "string", + "nullable": true } - } + }, + "additionalProperties": false }, - "RelationViewModel": { + "DatabaseSettings": { "type": "object", - "additionalProperties": false, "properties": { - "parentId": { + "id": { + "type": "string", + "format": "uuid" + }, + "sortOrder": { "type": "integer", - "readOnly": true, - "description": "Gets or sets the Parent Id of the Relation (Source).\n ", "format": "int32" }, - "parentName": { + "displayName": { "type": "string", - "readOnly": true, - "description": "Gets or sets the Parent Name of the relation (Source).\n ", "nullable": true }, - "childId": { - "type": "integer", - "readOnly": true, - "description": "Gets or sets the Child Id of the Relation (Destination).\n ", - "format": "int32" - }, - "childName": { + "defaultDatabaseName": { "type": "string", - "readOnly": true, - "description": "Gets or sets the Child Name of the relation (Destination).\n ", "nullable": true }, - "createDate": { + "providerName": { "type": "string", - "readOnly": true, - "description": "Gets or sets the date when the Relation was created.\n ", - "format": "date-time" + "nullable": true }, - "comment": { + "isConfigured": { + "type": "boolean" + }, + "requiresServer": { + "type": "boolean" + }, + "serverPlaceholder": { "type": "string", - "readOnly": true, - "description": "Gets or sets a comment for the Relation.\n ", "nullable": true - } - } - }, - "FolderTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isFolder": { - "type": "boolean" - } - } - } - ] - }, - "ProfilingStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { + "requiresCredentials": { + "type": "boolean" + }, + "supportsIntegratedAuthentication": { + "type": "boolean" + }, + "requiresConnectionTest": { "type": "boolean" } - } - }, - "OutOfDateStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "status": { - "$ref": "#/components/schemas/OutOfDateType" - } - } + }, + "additionalProperties": false }, - "OutOfDateType": { - "type": "integer", - "description": "", - "x-enumNames": [ - "OutOfDate", - "Current", - "Unknown" + "Dictionary": { + "required": [ + "name" ], - "enum": [ - 0, - 1, - 100 - ] - }, - "PagedViewModelOfContentTreeItemViewModel": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "parentId": { + "type": "string", + "format": "uuid", + "nullable": true }, - "items": { + "translations": { "type": "array", "items": { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - } - } - } - }, - "ContentTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" + "$ref": "#/components/schemas/DictionaryTranslation" + }, + "nullable": true }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "noAccess": { - "type": "boolean" - } - } + "contentApps": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentApp" + }, + "nullable": true + }, + "notifications": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BackOfficeNotification" + }, + "nullable": true, + "readOnly": true + }, + "name": { + "minLength": 1, + "type": "string" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "path": { + "type": "string", + "nullable": true } - ] + }, + "additionalProperties": false }, - "PagedViewModelOfRecycleBinItemViewModel": { + "DictionaryImport": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { + "dictionaryItems": { "type": "array", "items": { - "$ref": "#/components/schemas/RecycleBinItemViewModel" - } + "$ref": "#/components/schemas/DictionaryItemsImport" + }, + "nullable": true + }, + "tempFileName": { + "type": "string", + "nullable": true } - } + }, + "additionalProperties": false }, - "RecycleBinItemViewModel": { + "DictionaryItem": { "type": "object", - "additionalProperties": false, "properties": { - "key": { + "parentId": { "type": "string", - "format": "guid" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "hasChildren": { - "type": "boolean" - }, - "isContainer": { - "type": "boolean" + "format": "uuid", + "nullable": true }, - "parentKey": { + "key": { "type": "string", - "format": "guid", - "nullable": true + "format": "uuid" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfFolderTreeItemViewModel": { + "DictionaryItemsImport": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "name": { + "type": "string", + "nullable": true }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - } + "level": { + "type": "integer", + "format": "int32" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfLanguageViewModel": { + "DictionaryOverview": { "type": "object", - "additionalProperties": false, "properties": { - "total": { + "name": { + "type": "string", + "nullable": true + }, + "key": { + "type": "string", + "format": "uuid" + }, + "level": { "type": "integer", - "format": "int64" + "format": "int32" }, - "items": { + "translations": { "type": "array", "items": { - "$ref": "#/components/schemas/LanguageViewModel" - } + "$ref": "#/components/schemas/DictionaryTranslationOverview" + }, + "nullable": true, + "readOnly": true } - } + }, + "additionalProperties": false }, - "LanguageViewModel": { + "DictionaryTranslation": { "type": "object", - "additionalProperties": false, - "required": [ - "isoCode" - ], "properties": { "id": { "type": "integer", "format": "int32" }, - "isoCode": { + "key": { "type": "string", - "minLength": 1 + "format": "uuid" }, - "name": { + "displayName": { "type": "string", "nullable": true }, - "isDefault": { - "type": "boolean" + "isoCode": { + "type": "string", + "nullable": true }, - "isMandatory": { - "type": "boolean" + "translation": { + "type": "string", + "nullable": true }, - "fallbackLanguageId": { + "languageId": { "type": "integer", - "format": "int32", - "nullable": true + "format": "int32" } - } + }, + "additionalProperties": false }, - "InstallSettingsViewModel": { + "DictionaryTranslationOverview": { "type": "object", - "additionalProperties": false, "properties": { - "user": { - "$ref": "#/components/schemas/UserSettingsViewModel" + "displayName": { + "type": "string", + "nullable": true }, - "databases": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DatabaseSettingsViewModel" - } + "hasTranslation": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "UserSettingsViewModel": { + "DocumentBlueprintTreeItem": { "type": "object", - "additionalProperties": false, "properties": { - "minCharLength": { - "type": "integer", - "format": "int32" + "name": { + "type": "string", + "nullable": true }, - "minNonAlphaNumericLength": { - "type": "integer", - "format": "int32" - }, - "consentLevels": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ConsentLevelViewModel" - } - } - } - }, - "ConsentLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "level": { - "$ref": "#/components/schemas/TelemetryLevel" - }, - "description": { - "type": "string" - } - } - }, - "TelemetryLevel": { - "type": "string", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - "Minimal", - "Basic", - "Detailed" - ] - }, - "DatabaseSettingsViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { + "type": { "type": "string", - "format": "guid" - }, - "sortOrder": { - "type": "integer", - "format": "int32" - }, - "displayName": { - "type": "string" - }, - "defaultDatabaseName": { - "type": "string" - }, - "providerName": { - "type": "string" + "nullable": true }, - "isConfigured": { - "type": "boolean" + "icon": { + "type": "string", + "nullable": true }, - "requiresServer": { + "hasChildren": { "type": "boolean" }, - "serverPlaceholder": { - "type": "string" + "key": { + "type": "string", + "format": "uuid" }, - "requiresCredentials": { + "isContainer": { "type": "boolean" }, - "supportsIntegratedAuthentication": { - "type": "boolean" + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true }, - "requiresConnectionTest": { - "type": "boolean" - } - } - }, - "InstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "user", - "database" - ], - "properties": { - "user": { - "$ref": "#/components/schemas/UserInstallViewModel" + "documentTypeKey": { + "type": "string", + "format": "uuid" }, - "database": { - "$ref": "#/components/schemas/DatabaseInstallViewModel" + "documentTypeAlias": { + "type": "string", + "nullable": true }, - "telemetryLevel": { - "$ref": "#/components/schemas/TelemetryLevel" + "documentTypeName": { + "type": "string", + "nullable": true } - } + }, + "additionalProperties": false }, - "UserInstallViewModel": { + "DocumentTreeItem": { "type": "object", - "additionalProperties": false, - "required": [ - "name", - "email", - "password" - ], "properties": { "name": { "type": "string", - "maxLength": 255, - "minLength": 0 + "nullable": true }, - "email": { + "type": { "type": "string", - "format": "email", - "minLength": 1 + "nullable": true }, - "password": { + "icon": { "type": "string", - "minLength": 1 + "nullable": true }, - "subscribeToNewsletter": { + "hasChildren": { "type": "boolean" - } - } - }, - "DatabaseInstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "id", - "providerName" - ], - "properties": { - "id": { - "type": "string", - "format": "guid", - "minLength": 1 }, - "providerName": { + "key": { "type": "string", - "minLength": 1 + "format": "uuid" }, - "server": { + "isContainer": { + "type": "boolean" + }, + "parentKey": { "type": "string", + "format": "uuid", "nullable": true }, + "noAccess": { + "type": "boolean" + }, + "isProtected": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "isEdited": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DocumentTypeTreeItem": { + "type": "object", + "properties": { "name": { "type": "string", "nullable": true }, - "username": { + "type": { "type": "string", "nullable": true }, - "password": { + "icon": { "type": "string", "nullable": true }, - "useIntegratedAuthentication": { + "hasChildren": { "type": "boolean" }, - "connectionString": { + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { "type": "string", + "format": "uuid", "nullable": true - } - } - }, - "PagedViewModelOfHelpPageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/HelpPageViewModel" - } + "isFolder": { + "type": "boolean" + }, + "isElement": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "HelpPageViewModel": { + "EntityTreeItem": { "type": "object", - "additionalProperties": false, "properties": { "name": { "type": "string", "nullable": true }, - "description": { + "type": { "type": "string", "nullable": true }, - "url": { + "icon": { "type": "string", "nullable": true }, - "type": { + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { "type": "string", + "format": "uuid", "nullable": true } - } + }, + "additionalProperties": false + }, + "EventAttributes": { + "enum": [ + "None", + "SpecialName", + "RTSpecialName", + "ReservedMask" + ], + "type": "integer", + "format": "int32" }, - "PagedViewModelOfDocumentTreeItemViewModel": { + "EventInfo": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "name": { + "type": "string", + "nullable": true, + "readOnly": true }, - "items": { + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { "type": "array", "items": { - "$ref": "#/components/schemas/DocumentTreeItemViewModel" - } - } - } - }, - "DocumentTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/ContentTreeItemViewModel" + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isProtected": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "isEdited": { - "type": "boolean" - } - } - } - ] - }, - "PagedViewModelOfDocumentTypeTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { "type": "integer", - "format": "int64" + "format": "int32", + "readOnly": true }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTypeTreeItemViewModel" - } - } - } - }, - "DocumentTypeTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "memberType": { + "$ref": "#/components/schemas/MemberTypes" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isElement": { - "type": "boolean" - } - } - } - ] - }, - "DocumentBlueprintTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" + "attributes": { + "$ref": "#/components/schemas/EventAttributes" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "documentTypeKey": { - "type": "string", - "format": "guid" - }, - "documentTypeAlias": { - "type": "string" - }, - "documentTypeName": { - "type": "string", - "nullable": true - } - } + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "addMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "removeMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "raiseMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "isMulticast": { + "type": "boolean", + "readOnly": true + }, + "eventHandlerType": { + "$ref": "#/components/schemas/Type" } - ] + }, + "additionalProperties": false }, - "PagedViewModelOfDocumentBlueprintTreeItemViewModel": { + "Field": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "name": { + "type": "string", + "nullable": true }, - "items": { + "values": { "type": "array", "items": { - "$ref": "#/components/schemas/DocumentBlueprintTreeItemViewModel" - } + "type": "string" + }, + "nullable": true } - } + }, + "additionalProperties": false + }, + "FieldAttributes": { + "enum": [ + "PrivateScope", + "Private", + "FamANDAssem", + "Assembly", + "Family", + "FamORAssem", + "Public", + "FieldAccessMask", + "Static", + "InitOnly", + "Literal", + "NotSerialized", + "HasFieldRVA", + "SpecialName", + "RTSpecialName", + "HasFieldMarshal", + "PinvokeImpl", + "HasDefault", + "ReservedMask" + ], + "type": "integer", + "format": "int32" }, - "PagedViewModelOfDictionaryOverviewViewModel": { + "FieldInfo": { "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DictionaryOverviewViewModel" - } - } - } - }, - "DictionaryOverviewViewModel": { - "type": "object", - "additionalProperties": false, "properties": { "name": { "type": "string", - "description": "Gets or sets the key.\n ", - "nullable": true + "nullable": true, + "readOnly": true }, - "key": { - "type": "string", - "description": "Gets or sets the key.\n ", - "format": "guid" + "declaringType": { + "$ref": "#/components/schemas/Type" }, - "level": { - "type": "integer", - "description": "Gets or sets the level.\n ", - "format": "int32" + "reflectedType": { + "$ref": "#/components/schemas/Type" }, - "translations": { + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { "type": "array", - "description": "Sets the translations.\n ", "items": { - "$ref": "#/components/schemas/DictionaryTranslationOverviewViewModel" - } - } - } - }, - "DictionaryTranslationOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "displayName": { - "type": "string", - "description": "Gets or sets the display name.\n ", - "nullable": true + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true }, - "hasTranslation": { + "isCollectible": { "type": "boolean", - "description": "Gets or sets a value indicating whether has translation.\n " - } - } - }, - "DictionaryViewModel": { - "type": "object", - "description": "The dictionary display model\n ", - "additionalProperties": false, - "required": [ - "name" - ], - "properties": { - "parentId": { - "type": "string", - "description": "Gets or sets the parent id.\n ", - "format": "guid", - "nullable": true + "readOnly": true }, - "translations": { - "type": "array", - "description": "Gets or sets the translations.\n ", - "items": { - "$ref": "#/components/schemas/DictionaryTranslationViewModel" - } + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true }, - "contentApps": { - "type": "array", - "description": "Apps for the dictionary item\n ", - "items": { - "$ref": "#/components/schemas/ContentApp" - } + "memberType": { + "$ref": "#/components/schemas/MemberTypes" }, - "notifications": { - "type": "array", - "description": "This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.\n ", - "items": { - "$ref": "#/components/schemas/BackOfficeNotification" - } + "attributes": { + "$ref": "#/components/schemas/FieldAttributes" }, - "name": { - "type": "string", - "minLength": 1 + "fieldType": { + "$ref": "#/components/schemas/Type" }, - "key": { - "type": "string", - "description": "Gets or sets the Key for the object\n ", - "format": "guid" + "isInitOnly": { + "type": "boolean", + "readOnly": true }, - "path": { - "type": "string", - "description": "The path of the entity\n " + "isLiteral": { + "type": "boolean", + "readOnly": true + }, + "isNotSerialized": { + "type": "boolean", + "readOnly": true + }, + "isPinvokeImpl": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "fieldHandle": { + "$ref": "#/components/schemas/RuntimeFieldHandle" } - } + }, + "additionalProperties": false }, - "DictionaryTranslationViewModel": { + "FileSystemTreeItem": { "type": "object", - "additionalProperties": false, "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "key": { + "name": { "type": "string", - "format": "guid" + "nullable": true }, - "displayName": { + "type": { "type": "string", - "description": "Gets or sets the display name.\n ", "nullable": true }, - "isoCode": { + "icon": { "type": "string", - "description": "Gets or sets the ISO code.\n ", "nullable": true }, - "translation": { + "hasChildren": { + "type": "boolean" + }, + "path": { "type": "string", - "description": "Gets or sets the translation.\n " + "nullable": true }, - "languageId": { - "type": "integer", - "description": "Gets or sets the language id.\n ", - "format": "int32" + "isFolder": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "ContentApp": { + "FolderTreeItem": { "type": "object", - "description": "Represents a content app.\n ", - "additionalProperties": false, "properties": { "name": { "type": "string", - "description": "Gets the name of the content app.\n ", "nullable": true }, - "alias": { + "type": { "type": "string", - "description": "Gets the unique alias of the content app.\n ", "nullable": true }, - "weight": { - "type": "integer", - "description": "Gets or sets the weight of the content app.\n ", - "format": "int32" - }, "icon": { "type": "string", - "description": "Gets the icon of the content app.\n ", "nullable": true }, - "view": { - "type": "string", - "description": "Gets the view for rendering the content app.\n ", - "nullable": true + "hasChildren": { + "type": "boolean" }, - "viewModel": { - "description": "The view model specific to this app\n ", - "nullable": true + "key": { + "type": "string", + "format": "uuid" }, - "active": { - "type": "boolean", - "description": "Gets a value indicating whether the app is active.\n " + "isContainer": { + "type": "boolean" }, - "badge": { - "description": "Gets or sets the content app badge.\n ", - "nullable": true, - "oneOf": [ - { - "$ref": "#/components/schemas/ContentAppBadge" - } - ] - } - } - }, - "ContentAppBadge": { - "type": "object", - "description": "Represents a content app badge\n ", - "additionalProperties": false, - "properties": { - "count": { - "type": "integer", - "description": "Gets or sets the number displayed in the badge\n ", - "format": "int32" + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true }, - "type": { - "description": "Gets or sets the type of badge to display\n ", - "oneOf": [ - { - "$ref": "#/components/schemas/ContentAppBadgeType" - } - ] + "isFolder": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "ContentAppBadgeType": { - "type": "integer", - "description": "Represent the content app badge types\n ", - "x-enumNames": [ - "Default", - "Warning", - "Alert" - ], + "GenericParameterAttributes": { "enum": [ - 0, - 1, - 2 - ] + "None", + "Covariant", + "Contravariant", + "VarianceMask", + "ReferenceTypeConstraint", + "NotNullableValueTypeConstraint", + "DefaultConstructorConstraint", + "SpecialConstraintMask" + ], + "type": "integer", + "format": "int32" }, - "BackOfficeNotification": { + "HelpPage": { "type": "object", - "additionalProperties": false, "properties": { - "header": { + "name": { "type": "string", "nullable": true }, - "message": { + "description": { "type": "string", "nullable": true }, - "notificationType": { - "$ref": "#/components/schemas/NotificationStyle" + "url": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true } - } + }, + "additionalProperties": false }, - "NotificationStyle": { - "type": "integer", - "description": "", - "x-enumNames": [ - "Save", - "Info", - "Error", - "Success", - "Warning" - ], - "enum": [ - 0, - 1, - 2, - 3, - 4 - ] + "ICustomAttributeProvider": { + "type": "object", + "additionalProperties": false }, - "DictionaryItemViewModel": { + "IOutputFormatter": { + "type": "object", + "additionalProperties": false + }, + "Index": { "type": "object", - "additionalProperties": false, "properties": { - "parentId": { + "name": { "type": "string", - "format": "guid", "nullable": true }, - "key": { + "healthStatus": { + "type": "string", + "nullable": true + }, + "isHealthy": { + "type": "boolean", + "readOnly": true + }, + "canRebuild": { + "type": "boolean" + }, + "searcherName": { "type": "string", - "format": "guid" + "nullable": true + }, + "documentCount": { + "type": "integer", + "format": "int64" + }, + "fieldCount": { + "type": "integer", + "format": "int32" } - } + }, + "additionalProperties": false }, - "JsonPatchViewModel": { + "Install": { + "required": [ + "database", + "user" + ], "type": "object", - "additionalProperties": false, "properties": { - "op": { - "type": "string" + "user": { + "$ref": "#/components/schemas/UserInstall" }, - "path": { - "type": "string" + "database": { + "$ref": "#/components/schemas/DatabaseInstall" }, - "value": {} - } + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + }, + "additionalProperties": false }, - "DictionaryImportViewModel": { + "InstallSettings": { "type": "object", - "additionalProperties": false, "properties": { - "dictionaryItems": { + "user": { + "$ref": "#/components/schemas/UserSettings" + }, + "databases": { "type": "array", "items": { - "$ref": "#/components/schemas/DictionaryItemsImportViewModel" - } - }, - "tempFileName": { - "type": "string", + "$ref": "#/components/schemas/DatabaseSettings" + }, "nullable": true } - } + }, + "additionalProperties": false + }, + "IntPtr": { + "type": "object", + "additionalProperties": false }, - "DictionaryItemsImportViewModel": { + "JsonPatch": { "type": "object", - "additionalProperties": false, "properties": { - "name": { + "op": { "type": "string", "nullable": true }, - "level": { - "type": "integer", - "format": "int32" + "path": { + "type": "string", + "nullable": true + }, + "value": { + "nullable": true } - } + }, + "additionalProperties": false + }, + "Language": { + "required": [ + "isoCode" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "isoCode": { + "minLength": 1, + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "isDefault": { + "type": "boolean" + }, + "isMandatory": { + "type": "boolean" + }, + "fallbackLanguageId": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "LayoutKind": { + "enum": [ + "Sequential", + "Explicit", + "Auto" + ], + "type": "integer", + "format": "int32" + }, + "MemberInfo": { + "type": "object", + "properties": { + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "MemberTypes": { + "enum": [ + "Constructor", + "Event", + "Field", + "Method", + "Property", + "TypeInfo", + "Custom", + "NestedType", + "All" + ], + "type": "integer", + "format": "int32" + }, + "MethodAttributes": { + "enum": [ + "ReuseSlot", + "PrivateScope", + "Private", + "FamANDAssem", + "Assembly", + "Family", + "FamORAssem", + "Public", + "MemberAccessMask", + "UnmanagedExport", + "Static", + "Final", + "Virtual", + "HideBySig", + "NewSlot", + "VtableLayoutMask", + "CheckAccessOnOverride", + "Abstract", + "SpecialName", + "RTSpecialName", + "PinvokeImpl", + "HasSecurity", + "RequireSecObject", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "MethodBase": { + "type": "object", + "properties": { + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "MethodImplAttributes": { + "enum": [ + "IL", + "Managed", + "Native", + "OPTIL", + "Runtime", + "CodeTypeMask", + "Unmanaged", + "ManagedMask", + "NoInlining", + "ForwardRef", + "Synchronized", + "NoOptimization", + "PreserveSig", + "AggressiveInlining", + "AggressiveOptimization", + "InternalCall", + "MaxMethodImplVal" + ], + "type": "integer", + "format": "int32" + }, + "MethodInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "returnParameter": { + "$ref": "#/components/schemas/ParameterInfo" + }, + "returnType": { + "$ref": "#/components/schemas/Type" + }, + "returnTypeCustomAttributes": { + "$ref": "#/components/schemas/ICustomAttributeProvider" + } + }, + "additionalProperties": false + }, + "ModelsBuilder": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/components/schemas/ModelsMode" + }, + "canGenerate": { + "type": "boolean" + }, + "outOfDateModels": { + "type": "boolean" + }, + "lastError": { + "type": "string", + "nullable": true + }, + "version": { + "type": "string", + "nullable": true + }, + "modelsNamespace": { + "type": "string", + "nullable": true + }, + "trackingOutOfDateModels": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "ModelsMode": { + "enum": [ + "Nothing", + "InMemoryAuto", + "SourceCodeManual", + "SourceCodeAuto" + ], + "type": "integer", + "format": "int32" + }, + "Module": { + "type": "object", + "properties": { + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "fullyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "mdStreamVersion": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "moduleVersionId": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "scopeName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "moduleHandle": { + "$ref": "#/components/schemas/ModuleHandle" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "ModuleHandle": { + "type": "object", + "properties": { + "mdStreamVersion": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "NotFoundResult": { + "type": "object", + "properties": { + "statusCode": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "NotificationStyle": { + "enum": [ + "Save", + "Info", + "Error", + "Success", + "Warning" + ], + "type": "integer", + "format": "int32" + }, + "OkResult": { + "type": "object", + "properties": { + "statusCode": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "OutOfDateStatus": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/OutOfDateType" + } + }, + "additionalProperties": false + }, + "OutOfDateType": { + "enum": [ + "OutOfDate", + "Current", + "Unknown" + ], + "type": "integer", + "format": "int32" + }, + "PagedContentTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedCulture": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Culture" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDictionaryOverview": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryOverview" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentBlueprintTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentBlueprintTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentTypeTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedEntityTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedFileSystemTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedFolderTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedHelpPage": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HelpPage" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedIndex": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Index" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedLanguage": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Language" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedPaged": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PagedSearchResult" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRecycleBinItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecycleBinItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRelation": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Relation" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRelationItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedSearchResult": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchResult" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedSearcher": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Searcher" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedTelemetry": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Telemetry" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ParameterAttributes": { + "enum": [ + "None", + "In", + "Out", + "Lcid", + "Retval", + "Optional", + "HasDefault", + "HasFieldMarshal", + "Reserved3", + "Reserved4", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "ParameterInfo": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/components/schemas/ParameterAttributes" + }, + "member": { + "$ref": "#/components/schemas/MemberInfo" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "parameterType": { + "$ref": "#/components/schemas/Type" + }, + "position": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isIn": { + "type": "boolean", + "readOnly": true + }, + "isLcid": { + "type": "boolean", + "readOnly": true + }, + "isOptional": { + "type": "boolean", + "readOnly": true + }, + "isOut": { + "type": "boolean", + "readOnly": true + }, + "isRetval": { + "type": "boolean", + "readOnly": true + }, + "defaultValue": { + "nullable": true, + "readOnly": true + }, + "rawDefaultValue": { + "nullable": true, + "readOnly": true + }, + "hasDefaultValue": { + "type": "boolean", + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "ProblemDetails": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "status": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": { } + }, + "ProfilingStatus": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "PropertyAttributes": { + "enum": [ + "None", + "SpecialName", + "RTSpecialName", + "HasDefault", + "Reserved2", + "Reserved3", + "Reserved4", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "PropertyInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "propertyType": { + "$ref": "#/components/schemas/Type" + }, + "attributes": { + "$ref": "#/components/schemas/PropertyAttributes" + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "canRead": { + "type": "boolean", + "readOnly": true + }, + "canWrite": { + "type": "boolean", + "readOnly": true + }, + "getMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "setMethod": { + "$ref": "#/components/schemas/MethodInfo" + } + }, + "additionalProperties": false + }, + "RecycleBinItem": { + "type": "object", + "properties": { + "key": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + } + }, + "additionalProperties": false + }, + "Relation": { + "type": "object", + "properties": { + "parentId": { + "type": "integer", + "format": "int32" + }, + "parentName": { + "type": "string", + "nullable": true + }, + "childId": { + "type": "integer", + "format": "int32" + }, + "childName": { + "type": "string", + "nullable": true + }, + "createDate": { + "type": "string", + "format": "date-time" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "RelationItem": { + "type": "object", + "properties": { + "nodeKey": { + "type": "string", + "format": "uuid" + }, + "nodeName": { + "type": "string", + "nullable": true + }, + "nodeType": { + "type": "string", + "nullable": true + }, + "contentTypeIcon": { + "type": "string", + "nullable": true + }, + "contentTypeAlias": { + "type": "string", + "nullable": true + }, + "contentTypeName": { + "type": "string", + "nullable": true + }, + "relationTypeName": { + "type": "string", + "nullable": true + }, + "relationTypeIsBidirectional": { + "type": "boolean" + }, + "relationTypeIsDependency": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "RuntimeFieldHandle": { + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/IntPtr" + } + }, + "additionalProperties": false + }, + "RuntimeLevel": { + "enum": [ + "Unknown", + "Boot", + "Install", + "Upgrade", + "Run", + "BootFailed" + ], + "type": "integer", + "format": "int32" + }, + "RuntimeMethodHandle": { + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/IntPtr" + } + }, + "additionalProperties": false + }, + "RuntimeTypeHandle": { + "type": "object", + "properties": { + "value": { + "$ref": "#/components/schemas/IntPtr" + } + }, + "additionalProperties": false + }, + "SearchResult": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true + }, + "score": { + "type": "number", + "format": "float" + }, + "fieldCount": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Field" + }, + "nullable": true + } + }, + "additionalProperties": false }, - "PagedViewModelOfCultureViewModel": { + "Searcher": { "type": "object", - "additionalProperties": false, "properties": { - "total": { + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "SecurityRuleSet": { + "enum": [ + "None", + "Level1", + "Level2" + ], + "type": "integer", + "format": "int32" + }, + "ServerStatus": { + "type": "object", + "properties": { + "serverStatus": { + "$ref": "#/components/schemas/RuntimeLevel" + } + }, + "additionalProperties": false + }, + "StructLayoutAttribute": { + "type": "object", + "properties": { + "typeId": { + "nullable": true, + "readOnly": true + }, + "value": { + "$ref": "#/components/schemas/LayoutKind" + } + }, + "additionalProperties": false + }, + "Telemetry": { + "type": "object", + "properties": { + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + }, + "additionalProperties": false + }, + "TelemetryLevel": { + "enum": [ + "Minimal", + "Basic", + "Detailed" + ], + "type": "integer", + "format": "int32" + }, + "Type": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isInterface": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assemblyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "isNested": { + "type": "boolean", + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "declaringMethod": { + "$ref": "#/components/schemas/MethodBase" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "underlyingSystemType": { + "$ref": "#/components/schemas/Type" + }, + "isTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isArray": { + "type": "boolean", + "readOnly": true + }, + "isByRef": { + "type": "boolean", + "readOnly": true + }, + "isPointer": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isSZArray": { + "type": "boolean", + "readOnly": true + }, + "isVariableBoundArray": { + "type": "boolean", + "readOnly": true + }, + "isByRefLike": { + "type": "boolean", + "readOnly": true + }, + "hasElementType": { + "type": "boolean", + "readOnly": true + }, + "genericTypeArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "genericParameterPosition": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "genericParameterAttributes": { + "$ref": "#/components/schemas/GenericParameterAttributes" + }, + "attributes": { + "$ref": "#/components/schemas/TypeAttributes" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isImport": { + "type": "boolean", + "readOnly": true + }, + "isSealed": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isClass": { + "type": "boolean", + "readOnly": true + }, + "isNestedAssembly": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamANDAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamily": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamORAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedPrivate": { + "type": "boolean", + "readOnly": true + }, + "isNestedPublic": { + "type": "boolean", + "readOnly": true + }, + "isNotPublic": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isAutoLayout": { + "type": "boolean", + "readOnly": true + }, + "isExplicitLayout": { + "type": "boolean", + "readOnly": true + }, + "isLayoutSequential": { + "type": "boolean", + "readOnly": true + }, + "isAnsiClass": { + "type": "boolean", + "readOnly": true + }, + "isAutoClass": { + "type": "boolean", + "readOnly": true + }, + "isUnicodeClass": { + "type": "boolean", + "readOnly": true + }, + "isCOMObject": { + "type": "boolean", + "readOnly": true + }, + "isContextful": { + "type": "boolean", + "readOnly": true + }, + "isEnum": { + "type": "boolean", + "readOnly": true + }, + "isMarshalByRef": { + "type": "boolean", + "readOnly": true + }, + "isPrimitive": { + "type": "boolean", + "readOnly": true + }, + "isValueType": { + "type": "boolean", + "readOnly": true + }, + "isSignatureType": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "structLayoutAttribute": { + "$ref": "#/components/schemas/StructLayoutAttribute" + }, + "typeInitializer": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "typeHandle": { + "$ref": "#/components/schemas/RuntimeTypeHandle" + }, + "guid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "baseType": { + "$ref": "#/components/schemas/Type" + }, + "isSerializable": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "isVisible": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "TypeAttributes": { + "enum": [ + "NotPublic", + "AutoLayout", + "AnsiClass", + "Class", + "Public", + "NestedPublic", + "NestedPrivate", + "NestedFamily", + "NestedAssembly", + "NestedFamANDAssem", + "NestedFamORAssem", + "VisibilityMask", + "SequentialLayout", + "ExplicitLayout", + "LayoutMask", + "Interface", + "ClassSemanticsMask", + "Abstract", + "Sealed", + "SpecialName", + "RTSpecialName", + "Import", + "Serializable", + "WindowsRuntime", + "UnicodeClass", + "AutoClass", + "CustomFormatClass", + "StringFormatMask", + "HasSecurity", + "ReservedMask", + "BeforeFieldInit", + "CustomFormatMask" + ], + "type": "integer", + "format": "int32" + }, + "TypeInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isInterface": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assemblyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "isNested": { + "type": "boolean", + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "declaringMethod": { + "$ref": "#/components/schemas/MethodBase" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "underlyingSystemType": { + "$ref": "#/components/schemas/Type" + }, + "isTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isArray": { + "type": "boolean", + "readOnly": true + }, + "isByRef": { + "type": "boolean", + "readOnly": true + }, + "isPointer": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isSZArray": { + "type": "boolean", + "readOnly": true + }, + "isVariableBoundArray": { + "type": "boolean", + "readOnly": true + }, + "isByRefLike": { + "type": "boolean", + "readOnly": true + }, + "hasElementType": { + "type": "boolean", + "readOnly": true + }, + "genericTypeArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "genericParameterPosition": { "type": "integer", - "format": "int64" + "format": "int32", + "readOnly": true }, - "items": { + "genericParameterAttributes": { + "$ref": "#/components/schemas/GenericParameterAttributes" + }, + "attributes": { + "$ref": "#/components/schemas/TypeAttributes" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isImport": { + "type": "boolean", + "readOnly": true + }, + "isSealed": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isClass": { + "type": "boolean", + "readOnly": true + }, + "isNestedAssembly": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamANDAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamily": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamORAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedPrivate": { + "type": "boolean", + "readOnly": true + }, + "isNestedPublic": { + "type": "boolean", + "readOnly": true + }, + "isNotPublic": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isAutoLayout": { + "type": "boolean", + "readOnly": true + }, + "isExplicitLayout": { + "type": "boolean", + "readOnly": true + }, + "isLayoutSequential": { + "type": "boolean", + "readOnly": true + }, + "isAnsiClass": { + "type": "boolean", + "readOnly": true + }, + "isAutoClass": { + "type": "boolean", + "readOnly": true + }, + "isUnicodeClass": { + "type": "boolean", + "readOnly": true + }, + "isCOMObject": { + "type": "boolean", + "readOnly": true + }, + "isContextful": { + "type": "boolean", + "readOnly": true + }, + "isEnum": { + "type": "boolean", + "readOnly": true + }, + "isMarshalByRef": { + "type": "boolean", + "readOnly": true + }, + "isPrimitive": { + "type": "boolean", + "readOnly": true + }, + "isValueType": { + "type": "boolean", + "readOnly": true + }, + "isSignatureType": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "structLayoutAttribute": { + "$ref": "#/components/schemas/StructLayoutAttribute" + }, + "typeInitializer": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "typeHandle": { + "$ref": "#/components/schemas/RuntimeTypeHandle" + }, + "guid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "baseType": { + "$ref": "#/components/schemas/Type" + }, + "isSerializable": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "isVisible": { + "type": "boolean", + "readOnly": true + }, + "genericTypeParameters": { "type": "array", "items": { - "$ref": "#/components/schemas/CultureViewModel" - } + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "declaredConstructors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredEvents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredFields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FieldInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredMembers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredNestedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TypeInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyInfo" + }, + "nullable": true, + "readOnly": true + }, + "implementedInterfaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true } - } + }, + "additionalProperties": false + }, + "UpgradeSettings": { + "type": "object", + "properties": { + "currentState": { + "type": "string", + "nullable": true + }, + "newState": { + "type": "string", + "nullable": true + }, + "newVersion": { + "type": "string", + "nullable": true + }, + "oldVersion": { + "type": "string", + "nullable": true + }, + "reportUrl": { + "type": "string", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false }, - "CultureViewModel": { + "UserInstall": { + "required": [ + "email", + "name", + "password" + ], "type": "object", - "additionalProperties": false, "properties": { "name": { + "maxLength": 255, + "minLength": 0, "type": "string" }, - "englishName": { + "email": { + "minLength": 1, + "type": "string", + "format": "email" + }, + "password": { + "minLength": 1, "type": "string" + }, + "subscribeToNewsletter": { + "type": "boolean", + "readOnly": true } - } + }, + "additionalProperties": false }, - "PagedViewModelOfTelemetryLevel": { + "UserSettings": { "type": "object", - "additionalProperties": false, "properties": { - "total": { + "minCharLength": { "type": "integer", - "format": "int64" + "format": "int32" }, - "items": { + "minNonAlphaNumericLength": { + "type": "integer", + "format": "int32" + }, + "consentLevels": { "type": "array", "items": { - "$ref": "#/components/schemas/TelemetryLevel2" - } + "$ref": "#/components/schemas/ConsentLevel" + }, + "nullable": true } - } - }, - "TelemetryLevel2": { - "type": "integer", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - 0, - 1, - 2 - ] + }, + "additionalProperties": false }, - "AnalyticsLevelViewModel": { + "Version": { "type": "object", - "additionalProperties": false, "properties": { - "analyticsLevel": { - "$ref": "#/components/schemas/TelemetryLevel" + "version": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + }, + "securitySchemes": { + "OAuth": { + "type": "oauth2", + "description": "Umbraco Authentication", + "flows": { + "authorizationCode": { + "authorizationUrl": "/umbraco/management/api/v1.0/security/back-office/authorize", + "tokenUrl": "/umbraco/management/api/v1.0/security/back-office/token", + "scopes": { } } } } } }, - "tags": [ - { - "name": "Analytics" - }, - { - "name": "Culture" - }, - { - "name": "DataType" - }, - { - "name": "Dictionary" - }, - { - "name": "DictionaryItem" - }, - { - "name": "Document" - }, - { - "name": "DocumentBlueprint" - }, - { - "name": "DocumentType" - }, - { - "name": "Help" - }, - { - "name": "Install" - }, - { - "name": "Language" - }, - { - "name": "Media" - }, - { - "name": "MediaType" - }, - { - "name": "MemberGroup" - }, - { - "name": "MemberType" - }, - { - "name": "ModelsBuilder" - }, - { - "name": "PartialView" - }, - { - "name": "Profiling" - }, - { - "name": "PublishedCache" - }, - { - "name": "Relation" - }, - { - "name": "RelationType" - }, - { - "name": "Script" - }, - { - "name": "Search" - }, - { - "name": "Server" - }, - { - "name": "StaticFile" - }, - { - "name": "Stylesheet" - }, - { - "name": "Template" - }, - { - "name": "TrackedReferences" - }, + "security": [ { - "name": "Upgrade" + "OAuth": [ ] } ] } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs new file mode 100644 index 000000000000..2b8dd3f4696e --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.Serialization; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +public class EnumSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema model, SchemaFilterContext context) + { + if (context.Type.IsEnum) + { + model.Enum.Clear(); + foreach (var name in Enum.GetNames(context.Type)) + { + var actualName = context.Type.GetField(name)?.GetCustomAttribute()?.Value ?? name; + model.Enum.Add(new OpenApiString(actualName)); + } + } + } +} diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs new file mode 100644 index 000000000000..f49a89e22f36 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs @@ -0,0 +1,33 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Umbraco.Extensions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +/// +/// This filter explicitly removes all other mime types than application/json from the produced OpenAPI document +/// +public class MimeTypeDocumentFilter : IDocumentFilter +{ + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + OpenApiOperation[] operations = swaggerDoc.Paths + .SelectMany(path => path.Value.Operations.Values) + .ToArray(); + + void RemoveUnwantedMimeTypes(IDictionary content) => + content.RemoveAll(r => r.Key != "application/json"); + + OpenApiRequestBody[] requestBodies = operations.Select(operation => operation.RequestBody).WhereNotNull().ToArray(); + foreach (OpenApiRequestBody requestBody in requestBodies) + { + RemoveUnwantedMimeTypes(requestBody.Content); + } + + OpenApiResponse[] responses = operations.SelectMany(operation => operation.Responses.Values).WhereNotNull().ToArray(); + foreach (OpenApiResponse response in responses) + { + RemoveUnwantedMimeTypes(response.Content); + } + } +} diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs new file mode 100644 index 000000000000..71c15620fd7c --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs @@ -0,0 +1,26 @@ +using System.Text.RegularExpressions; +using Umbraco.Extensions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +internal static class SchemaIdGenerator +{ + public static string Generate(Type type) + { + string SanitizedTypeName(Type t) => t.Name + // first grab the "non generic" part of any generic type name (i.e. "PagedViewModel`1" becomes "PagedViewModel") + .Split('`').First() + // then remove the "ViewModel" postfix from type names + .TrimEnd("ViewModel"); + + var name = SanitizedTypeName(type); + if (type.IsGenericType) + { + // append the generic type names, ultimately turning i.e. "PagedViewModel" into "PagedRelationItem" + name += string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName)); + } + + // make absolutely sure we don't pass any invalid named by removing all non-word chars + return Regex.Replace(name, @"[^\w]", string.Empty); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs new file mode 100644 index 000000000000..87f63f2f6eed --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs @@ -0,0 +1,131 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; +using Umbraco.New.Cms.Core; +using Umbraco.New.Cms.Core.Models.Configuration; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.Security; + +public class BackOfficeApplicationManager : IBackOfficeApplicationManager +{ + private readonly IOpenIddictApplicationManager _applicationManager; + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly Uri? _backOfficeHost; + + public BackOfficeApplicationManager( + IOpenIddictApplicationManager applicationManager, + IWebHostEnvironment webHostEnvironment, + IOptionsMonitor securitySettingsMonitor) + { + _applicationManager = applicationManager; + _webHostEnvironment = webHostEnvironment; + _backOfficeHost = securitySettingsMonitor.CurrentValue.BackOfficeHost; + } + + public async Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default) + { + if (backOfficeUrl.IsAbsoluteUri is false) + { + throw new ArgumentException($"Expected an absolute URL, got: {backOfficeUrl}", nameof(backOfficeUrl)); + } + + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco back-office access", + ClientId = Constants.OauthClientIds.BackOffice, + RedirectUris = + { + CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, "/umbraco/login/callback/") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.GrantTypes.RefreshToken, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + + if (_webHostEnvironment.IsProduction()) + { + await Delete(Constants.OauthClientIds.Swagger, cancellationToken); + await Delete(Constants.OauthClientIds.Postman, cancellationToken); + } + else + { + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco Swagger access", + ClientId = Constants.OauthClientIds.Swagger, + RedirectUris = + { + CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco Postman access", + ClientId = Constants.OauthClientIds.Postman, + RedirectUris = + { + new Uri("https://oauth.pstmn.io/v1/callback") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + } + } + + private async Task CreateOrUpdate(OpenIddictApplicationDescriptor clientDescriptor, CancellationToken cancellationToken) + { + var identifier = clientDescriptor.ClientId ?? + throw new ApplicationException($"ClientId is missing for application: {clientDescriptor.DisplayName ?? "(no name)"}"); + var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken); + if (client is null) + { + await _applicationManager.CreateAsync(clientDescriptor, cancellationToken); + } + else + { + await _applicationManager.UpdateAsync(client, clientDescriptor, cancellationToken); + } + } + + private async Task Delete(string identifier, CancellationToken cancellationToken) + { + var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken); + if (client is null) + { + return; + } + + await _applicationManager.DeleteAsync(client, cancellationToken); + } + + private static Uri CallbackUrlFor(Uri url, string relativePath) => new Uri( $"{url.GetLeftPart(UriPartial.Authority)}/{relativePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}"); +} diff --git a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj index 592f328ec29e..37c95e98eed1 100644 --- a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj +++ b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj @@ -10,7 +10,12 @@ - + + + + + + diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs deleted file mode 100644 index 73d442992ea9..000000000000 --- a/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.ManagementApi.ViewModels.Analytics; - -public class AnalyticsLevelViewModel -{ - [JsonConverter(typeof(JsonStringEnumConverter))] - public TelemetryLevel AnalyticsLevel { get; set; } -} diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs new file mode 100644 index 000000000000..574ae863601f --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +public class TelemetryViewModel +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + public TelemetryLevel TelemetryLevel { get; set; } +} diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj index 4431cf255829..7d477069edd5 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index 44c9c37dfd8a..be13776da8e9 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -1,8 +1,11 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Core.Configuration.Grid; @@ -13,9 +16,27 @@ public GridConfig( IManifestParser manifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) => EditorsConfig = - new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger()); + new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger(), gridEditorsConfigFileProviderFactory); + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public GridConfig( + AppCaches appCaches, + IManifestParser manifestParser, + IJsonSerializer jsonSerializer, + IHostingEnvironment hostingEnvironment, + ILoggerFactory loggerFactory) + : this( + appCaches, + manifestParser, + jsonSerializer, + hostingEnvironment, + loggerFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { + } public IGridEditorsConfig EditorsConfig { get; } } diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index 11ae329192fb..ab0ec8b18209 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -1,12 +1,15 @@ -using System.Reflection; using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Core.Configuration.Grid; @@ -17,6 +20,7 @@ internal class GridEditorsConfig : IGridEditorsConfig private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; + private readonly IGridEditorsConfigFileProviderFactory _gridEditorsConfigFileProviderFactory; private readonly IManifestParser _manifestParser; public GridEditorsConfig( @@ -24,13 +28,32 @@ public GridEditorsConfig( IHostingEnvironment hostingEnvironment, IManifestParser manifestParser, IJsonSerializer jsonSerializer, - ILogger logger) + ILogger logger, + IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) { _appCaches = appCaches; _hostingEnvironment = hostingEnvironment; _manifestParser = manifestParser; _jsonSerializer = jsonSerializer; _logger = logger; + _gridEditorsConfigFileProviderFactory = gridEditorsConfigFileProviderFactory; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public GridEditorsConfig( + AppCaches appCaches, + IHostingEnvironment hostingEnvironment, + IManifestParser manifestParser, + IJsonSerializer jsonSerializer, + ILogger logger) + : this( + appCaches, + hostingEnvironment, + manifestParser, + jsonSerializer, + logger, + StaticServiceProvider.Instance.GetRequiredService()) + { } public IEnumerable Editors @@ -39,13 +62,37 @@ public IEnumerable Editors { List GetResult() { - var configFolder = - new DirectoryInfo(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Config)); + IFileInfo? gridConfig = null; var editors = new List(); - var gridConfig = Path.Combine(configFolder.FullName, "grid.editors.config.js"); - if (File.Exists(gridConfig)) + var configPath = Constants.SystemDirectories.Config.TrimStart(Constants.CharArrays.Tilde); + + // Get physical file if it exists + var configPhysicalDirPath = _hostingEnvironment.MapPathContentRoot(configPath); + + if (Directory.Exists(configPhysicalDirPath) == true) + { + var physicalFileProvider = new PhysicalFileProvider(configPhysicalDirPath); + gridConfig = GetConfigFile(physicalFileProvider, string.Empty); + } + + // If there is no physical file, check in RCLs + if (gridConfig is null) + { + IFileProvider? compositeFileProvider = _gridEditorsConfigFileProviderFactory.Create(); + + if (compositeFileProvider is null) + { + throw new ArgumentNullException(nameof(compositeFileProvider)); + } + + gridConfig = GetConfigFile(compositeFileProvider, configPath); + } + + if (gridConfig is not null) { - var sourceString = File.ReadAllText(gridConfig); + using Stream stream = gridConfig.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var sourceString = reader.ReadToEnd(); try { @@ -63,19 +110,16 @@ List GetResult() // Read default from embedded file else { - Assembly assembly = GetType().Assembly; - Stream? resourceStream = assembly.GetManifestResourceStream( - "Umbraco.Cms.Core.EmbeddedResources.Grid.grid.editors.config.js"); + IFileProvider configFileProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Core.EmbeddedResources.Grid"); + IFileInfo embeddedConfig = configFileProvider.GetFileInfo("grid.editors.config.js"); - if (resourceStream is not null) - { - using var reader = new StreamReader(resourceStream, Encoding.UTF8); - var sourceString = reader.ReadToEnd(); - editors.AddRange(_jsonSerializer.Deserialize>(sourceString)!); - } + using Stream stream = embeddedConfig.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var sourceString = reader.ReadToEnd(); + editors.AddRange(_jsonSerializer.Deserialize>(sourceString)!); } - // add manifest editors, skip duplicates + // Add manifest editors, skip duplicates foreach (GridEditor gridEditor in _manifestParser.CombinedManifest.GridEditors) { if (editors.Contains(gridEditor) == false) @@ -95,4 +139,10 @@ List GetResult() return result!; } } + + private static IFileInfo? GetConfigFile(IFileProvider fileProvider, string path) + { + IFileInfo fileInfo = fileProvider.GetFileInfo($"{path}/grid.editors.config.js"); + return fileInfo.Exists ? fileInfo : null; + } } diff --git a/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs b/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs new file mode 100644 index 000000000000..f8de1d7265b0 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs @@ -0,0 +1,11 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Configuration.Models; + +/// +/// Typed configuration options for license settings. +/// +public class LicensesSettings : Dictionary +{ +} diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index bbeae780d847..6eb184fb40a1 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -63,6 +63,12 @@ public static class Routing public const string AreaToken = "area"; } + public static class RoutePath + { + public const string Tree = "tree"; + public const string RecycleBin = "recycle-bin"; + } + public static class AttributeRouting { public const string BackOfficeToken = "umbracoBackOffice"; diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index 31ef06c4002f..3be3815afa47 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.DependencyInjection; /// public static partial class UmbracoBuilderExtensions { - private static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null) + internal static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null) where TOptions : class { UmbracoOptionsAttribute? umbracoOptionsAttribute = typeof(TOptions).GetCustomAttribute(); diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs index cc2415f4cde1..0d354b65de80 100644 --- a/src/Umbraco.Core/Deploy/ArtifactBase.cs +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -1,50 +1,49 @@ -namespace Umbraco.Cms.Core.Deploy +namespace Umbraco.Cms.Core.Deploy; + +/// +/// Provides a base class to all artifacts. +/// +public abstract class ArtifactBase : IArtifact + where TUdi : Udi { - /// - /// Provides a base class to all artifacts. - /// - public abstract class ArtifactBase : IArtifact - where TUdi : Udi + protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null) { - protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null) - { - Udi = udi ?? throw new ArgumentNullException("udi"); - Name = Udi.ToString(); + Udi = udi ?? throw new ArgumentNullException("udi"); + Name = Udi.ToString(); - _dependencies = dependencies ?? Enumerable.Empty(); - _checksum = new Lazy(GetChecksum); - } - - private readonly Lazy _checksum; + _dependencies = dependencies ?? Enumerable.Empty(); + _checksum = new Lazy(GetChecksum); + } - private IEnumerable _dependencies; + private readonly Lazy _checksum; - protected abstract string GetChecksum(); + private IEnumerable _dependencies; - Udi IArtifactSignature.Udi => Udi; + protected abstract string GetChecksum(); - public TUdi Udi { get; set; } + Udi IArtifactSignature.Udi => Udi; - public string Checksum => _checksum.Value; + public TUdi Udi { get; set; } - /// - /// Prevents the property from being serialized. - /// - /// - /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] - /// as that would require an external dependency in Umbraco.Cms.Core. - /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm - /// - public bool ShouldSerializeChecksum() => false; + public string Checksum => _checksum.Value; - public IEnumerable Dependencies - { - get => _dependencies; - set => _dependencies = value.OrderBy(x => x.Udi); - } + /// + /// Prevents the property from being serialized. + /// + /// + /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] + /// as that would require an external dependency in Umbraco.Cms.Core. + /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm + /// + public bool ShouldSerializeChecksum() => false; + + public IEnumerable Dependencies + { + get => _dependencies; + set => _dependencies = value.OrderBy(x => x.Udi); + } - public string Name { get; set; } + public string Name { get; set; } - public string Alias { get; set; } = string.Empty; - } + public string Alias { get; set; } = string.Empty; } diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs index 1b75fe11c052..e2dd343af1aa 100644 --- a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs +++ b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs @@ -44,3 +44,40 @@ public static ArtifactDeployState Create /// protected abstract IArtifact GetArtifactAsIArtifact(); } + +/// +/// Represent the state of an artifact being deployed. +/// +/// The type of the artifact. +/// The type of the entity. +public class ArtifactDeployState : ArtifactDeployState + where TArtifact : IArtifact +{ + /// + /// Initializes a new instance of the class. + /// + /// The artifact. + /// The entity. + /// The service connector deploying the artifact. + /// The next pass number. + public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass) + { + Artifact = art; + Entity = entity; + Connector = connector; + NextPass = nextPass; + } + + /// + /// Gets or sets the artifact. + /// + public new TArtifact Artifact { get; set; } + + /// + /// Gets or sets the entity. + /// + public TEntity? Entity { get; set; } + + /// + protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact; +} diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs deleted file mode 100644 index 0ff1e20e8766..000000000000 --- a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Umbraco.Cms.Core.Deploy; - -/// -/// Represent the state of an artifact being deployed. -/// -/// The type of the artifact. -/// The type of the entity. -public class ArtifactDeployState : ArtifactDeployState - where TArtifact : IArtifact -{ - /// - /// Initializes a new instance of the class. - /// - /// The artifact. - /// The entity. - /// The service connector deploying the artifact. - /// The next pass number. - public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass) - { - Artifact = art; - Entity = entity; - Connector = connector; - NextPass = nextPass; - } - - /// - /// Gets or sets the artifact. - /// - public new TArtifact Artifact { get; set; } - - /// - /// Gets or sets the entity. - /// - public TEntity? Entity { get; set; } - - /// - protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact; -} diff --git a/src/Umbraco.Core/Deploy/IContextCache.cs b/src/Umbraco.Core/Deploy/IContextCache.cs new file mode 100644 index 000000000000..d17506490558 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IContextCache.cs @@ -0,0 +1,31 @@ +namespace Umbraco.Cms.Core.Deploy; + +/// +/// Represents a context cache used by Deploy operations. +/// +public interface IContextCache +{ + /// + /// Creates the item on the context cache using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The item. + void Create(string key, T item); + + /// + /// Gets an item from the context cache or creates and stores it using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The factory method to create the item (if it doesn't exist yet). + /// + /// The item. + /// + T? GetOrCreate(string key, Func factory); + + /// + /// Clears all cached items on this context. + /// + void Clear(); +} diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs index 6b91926b578c..506d1f5745ae 100644 --- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs +++ b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs @@ -1,37 +1,67 @@ -using System.Diagnostics.CodeAnalysis; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert data type configuration to / from an environment-agnostic string. +/// Defines methods that can convert data type configuration to / from an environment-agnostic string. /// /// -/// Configuration may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Configuration may contain values such as content identifiers, that would be local +/// to one environment, and need to be converted in order to be deployed. /// -[SuppressMessage( - "ReSharper", - "UnusedMember.Global", - Justification = "This is actual only used by Deploy, but we don't want third parties to have references on deploy, that's why this interface is part of core.")] public interface IDataTypeConfigurationConnector { /// - /// Gets the property editor aliases that the value converter supports by default. + /// Gets the property editor aliases that the value converter supports by default. /// + /// + /// The property editor aliases. + /// IEnumerable PropertyEditorAliases { get; } /// - /// Gets the artifact datatype configuration corresponding to the actual datatype configuration. + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. /// - /// The datatype. + /// The data type. /// The dependencies. - string? ToArtifact(IDataType dataType, ICollection dependencies); + /// + /// The artifact configuration value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? ToArtifact(IDataType dataType, ICollection dependencies) + => ToArtifact(dataType, dependencies, PassThroughCache.Instance); /// - /// Gets the actual datatype configuration corresponding to the artifact configuration. + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. /// - /// The datatype. - /// The artifact configuration. - object? FromArtifact(IDataType dataType, string? configuration); + /// The data type. + /// The dependencies. + /// The context cache. + /// + /// The artifact configuration value. + /// + string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// + /// The data type configuration. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + object? FromArtifact(IDataType dataType, string? configuration) + => FromArtifact(dataType, configuration, PassThroughCache.Instance); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// The context cache. + /// + /// The data type configuration. + /// + object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs index f6cd7c80024f..b4f530fb35d9 100644 --- a/src/Umbraco.Core/Deploy/IServiceConnector.cs +++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs @@ -3,34 +3,65 @@ namespace Umbraco.Cms.Core.Deploy; /// -/// Connects to an Umbraco service. +/// Connects to an Umbraco service. /// +/// public interface IServiceConnector : IDiscoverable { /// - /// Gets an artifact. + /// Gets an artifact. /// /// The entity identifier of the artifact. - /// The corresponding artifact, or null. - IArtifact? GetArtifact(Udi udi); + /// + /// The corresponding artifact, or null. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + IArtifact? GetArtifact(Udi udi) + => GetArtifact(udi, PassThroughCache.Instance); /// - /// Gets an artifact. + /// Gets an artifact. + /// + /// The entity identifier of the artifact. + /// The context cache. + /// + /// The corresponding artifact, or null. + /// + IArtifact? GetArtifact(Udi udi, IContextCache contextCache); + + /// + /// Gets an artifact. + /// + /// The entity. + /// + /// The corresponding artifact. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + IArtifact GetArtifact(object entity) + => GetArtifact(entity, PassThroughCache.Instance); + + /// + /// Gets an artifact. /// /// The entity. - /// The corresponding artifact. - IArtifact GetArtifact(object entity); + /// The context cache. + /// + /// The corresponding artifact. + /// + IArtifact GetArtifact(object entity, IContextCache contextCache); /// - /// Initializes processing for an artifact. + /// Initializes processing for an artifact. /// /// The artifact. /// The deploy context. - /// The mapped artifact. + /// + /// The mapped artifact. + /// ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context); /// - /// Processes an artifact. + /// Processes an artifact. /// /// The mapped artifact. /// The deploy context. @@ -38,46 +69,56 @@ public interface IServiceConnector : IDiscoverable void Process(ArtifactDeployState dart, IDeployContext context, int pass); /// - /// Explodes a range into udis. + /// Explodes a range into udis. /// /// The range. /// The list of udis where to add the new udis. - /// Also, it's cool to have a method named Explode. Kaboom! + /// + /// Also, it's cool to have a method named Explode. Kaboom! + /// void Explode(UdiRange range, List udis); /// - /// Gets a named range for a specified udi and selector. + /// Gets a named range for a specified udi and selector. /// /// The udi. /// The selector. - /// The named range for the specified udi and selector. + /// + /// The named range for the specified udi and selector. + /// NamedUdiRange GetRange(Udi udi, string selector); /// - /// Gets a named range for specified entity type, identifier and selector. + /// Gets a named range for specified entity type, identifier and selector. /// /// The entity type. /// The identifier. /// The selector. - /// The named range for the specified entity type, identifier and selector. + /// + /// The named range for the specified entity type, identifier and selector. + /// /// - /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? - /// - /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do - /// not manage guids but only ints... so we have to provide a way to support it. The string id here - /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to - /// indicate the "root" i.e. an open udi. - /// + /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? + /// + /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do + /// not manage guids but only ints... so we have to provide a way to support it. The string id here + /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to + /// indicate the "root" i.e. an open udi. + /// /// NamedUdiRange GetRange(string entityType, string sid, string selector); /// - /// Compares two artifacts. + /// Compares two artifacts. /// /// The first artifact. /// The second artifact. /// A collection of differences to append to, if not null. - /// A boolean value indicating whether the artifacts are identical. - /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + /// + /// A boolean value indicating whether the artifacts are identical. + /// + /// + /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + /// bool Compare(IArtifact? art1, IArtifact? art2, ICollection? differences = null); } diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs index f2a776c7ca3a..9f3b17f71c49 100644 --- a/src/Umbraco.Core/Deploy/IValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -3,35 +3,70 @@ namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert a property value to / from an environment-agnostic string. +/// Defines methods that can convert a property value to and from an environment-agnostic string. /// /// -/// Property values may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. Connectors also deal -/// with serializing to / from string. +/// Property values may contain values such as content identifiers, that would be local +/// to one environment and need to be converted in order to be deployed. Connectors also deal +/// with serializing and deserializing the content value to an environment-agnostic string. /// public interface IValueConnector { /// - /// Gets the property editor aliases that the value converter supports by default. + /// Gets the property editor aliases that the value converter supports by default. /// + /// + /// The property editor aliases. + /// IEnumerable PropertyEditorAliases { get; } /// - /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. /// /// The content property value. /// The value property type /// The content dependencies. - /// The deploy property value. - string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies); + /// + /// The deploy property value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies) + => ToArtifact(value, propertyType, dependencies, PassThroughCache.Instance); /// - /// Gets the content property value corresponding to a deploy property value. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// + /// The content property value. + /// The value property type + /// The content dependencies. + /// The context cache. + /// + /// The deploy property value. + /// + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache); + + /// + /// Gets the content property value corresponding to a deploy property value. + /// + /// The deploy property value. + /// The value property type + /// The current content property value. + /// + /// The content property value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue) + => FromArtifact(value, propertyType, currentValue, PassThroughCache.Instance); + + /// + /// Gets the content property value corresponding to a deploy property value. /// /// The deploy property value. /// The value property type /// The current content property value. - /// The content property value. - object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue); + /// The context cache. + /// + /// The content property value. + /// + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/PassThroughCache.cs b/src/Umbraco.Core/Deploy/PassThroughCache.cs new file mode 100644 index 000000000000..064fc10ad5eb --- /dev/null +++ b/src/Umbraco.Core/Deploy/PassThroughCache.cs @@ -0,0 +1,34 @@ +namespace Umbraco.Cms.Core.Deploy; + +/// +/// A pass through context cache that always creates the items. +/// +/// +public sealed class PassThroughCache : IContextCache +{ + /// + /// Gets the instance. + /// + /// + /// The instance. + /// + public static PassThroughCache Instance { get; } = new PassThroughCache(); + + /// + /// Prevents a default instance of the class from being created. + /// + private PassThroughCache() + { } + + /// + public void Create(string key, T item) + { } + + /// + public T? GetOrCreate(string key, Func factory) + => factory(); + + /// + public void Clear() + { } +} diff --git a/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs b/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs new file mode 100644 index 000000000000..2c401a3f9935 --- /dev/null +++ b/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the grid.editors.config.js file. +/// +public interface IGridEditorsConfigFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs b/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs new file mode 100644 index 000000000000..982b029c27d3 --- /dev/null +++ b/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the package.manifest file. +/// +public interface IManifestFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs index fe1e83d2545f..960be851ff38 100644 --- a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs +++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing; @@ -19,11 +21,18 @@ public class UmbracoRequestPaths private readonly string _mvcArea; private readonly string _previewMvcPath; private readonly string _surfaceMvcPath; + private readonly IOptions _umbracoRequestPathsOptions; + + [Obsolete("Use constructor that takes IOptions - Will be removed in Umbraco 13")] + public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + : this(globalSettings, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService>()) + { + } /// /// Initializes a new instance of the class. /// - public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment, IOptions umbracoRequestPathsOptions) { var applicationPath = hostingEnvironment.ApplicationVirtualPath; _appPath = applicationPath.TrimStart(Constants.CharArrays.ForwardSlash); @@ -38,6 +47,7 @@ public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvi _surfaceMvcPath = "/" + _mvcArea + "/Surface/"; _apiMvcPath = "/" + _mvcArea + "/Api/"; _installPath = hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install); + _umbracoRequestPathsOptions = umbracoRequestPathsOptions; } /// @@ -51,6 +61,7 @@ public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvi /// These are def back office: /// /Umbraco/BackOffice = back office /// /Umbraco/Preview = back office + /// /Umbraco/Management/Api = back office /// /// /// If it's not any of the above then we cannot determine if it's back office or front-end @@ -99,6 +110,11 @@ public bool IsBackOfficeRequest(string absPath) return false; } + if (_umbracoRequestPathsOptions.Value.IsBackOfficeRequest(urlPath)) + { + return true; + } + // if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by // checking how many parts the route has, for example, all PluginController routes will be routed like // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs b/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs new file mode 100644 index 000000000000..91f13eab3bc2 --- /dev/null +++ b/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Routing; + +public class UmbracoRequestPathsOptions +{ + /// + /// Gets the delegate that allows us to handle additional URLs as back-office requests. + /// This returns false by default and can be overwritten in Startup.cs. + /// + public Func IsBackOfficeRequest { get; set; } = _ => false; +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 03a292c880e6..dc480b160f14 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -47,6 +47,10 @@ <_Parameter1>DynamicProxyGenAssembly2 + + + <_Parameter1>Umbraco.Cms.ManagementApi + diff --git a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs index eb1bc518acaa..a9ed5fd84df5 100644 --- a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs +++ b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs @@ -1,44 +1,77 @@ using Umbraco.Cms.Core.Models; +using static Umbraco.Cms.Core.Models.GridValue; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert a grid cell value to / from an environment-agnostic string. +/// Defines methods that can convert a grid cell value to / from an environment-agnostic string. /// /// -/// Grid cell values may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Grid cell values may contain values such as content identifiers, that would be local +/// to one environment, and need to be converted in order to be deployed. /// public interface IGridCellValueConnector { /// - /// Gets a value indicating whether the connector supports a specified grid editor view. + /// Gets a value indicating whether the connector supports a specified grid editor view. /// - /// - /// The grid editor view. It needs to be the view instead of the alias as the view is really what - /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases - /// using the same kind of view. - /// - /// A value indicating whether the connector supports the grid editor view. - /// Note that can be string.Empty to indicate the "default" connector. + /// The grid editor view. It needs to be the view instead of the alias as the view is really what + /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases + /// using the same kind of view. + /// + /// true if the specified view is connector; otherwise, false. + /// + /// + /// A value indicating whether the connector supports the grid editor view. + /// bool IsConnector(string view); /// - /// Gets the value to be deployed from the control value as a string. + /// Gets the value to be deployed from the control value as a string. /// /// The control containing the value. /// The dependencies of the property. - /// The grid cell value to be deployed. - /// Note that - string? GetValue(GridValue.GridControl gridControl, ICollection dependencies); + /// + /// The grid cell value to be deployed. + /// + /// + /// Note that + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? GetValue(GridValue.GridControl gridControl, ICollection dependencies) + => GetValue(gridControl, dependencies, PassThroughCache.Instance); + + /// + /// Gets the value to be deployed from the control value as a string. + /// + /// The control containing the value. + /// The dependencies of the property. + /// The context cache. + /// + /// The grid cell value to be deployed. + /// + string? GetValue(GridValue.GridControl gridControl, ICollection dependencies, IContextCache contextCache); + + /// + /// Allows you to modify the value of a control being deployed. + /// + /// The control being deployed. + /// + /// Follows the pattern of the property value connectors (). + /// The SetValue method is used to modify the value of the . + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + void SetValue(GridValue.GridControl gridControl) + => SetValue(gridControl, PassThroughCache.Instance); /// - /// Allows you to modify the value of a control being deployed. + /// Allows you to modify the value of a control being deployed. /// /// The control being deployed. + /// The context cache. /// - /// Follows the pattern of the property value connectors (). The SetValue method is - /// used to modify the value of the . + /// Follows the pattern of the property value connectors (). + /// The SetValue method is used to modify the value of the . /// - void SetValue(GridValue.GridControl gridControl); + void SetValue(GridValue.GridControl gridControl, IContextCache contextCache); } diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 4dbd6abd4066..887ac05dc49c 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -1,13 +1,17 @@ using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Manifest; @@ -21,6 +25,7 @@ public class ManifestParser : IManifestParser private readonly IAppPolicyCache _cache; private readonly IDataValueEditorFactory _dataValueEditorFactory; + private readonly IManifestFileProviderFactory _manifestFileProviderFactory; private readonly ManifestFilterCollection _filters; private readonly IHostingEnvironment _hostingEnvironment; @@ -46,7 +51,8 @@ public ManifestParser( IJsonSerializer jsonSerializer, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, - IDataValueEditorFactory dataValueEditorFactory) + IDataValueEditorFactory dataValueEditorFactory, + IManifestFileProviderFactory manifestFileProviderFactory) { if (appCaches == null) { @@ -64,6 +70,34 @@ public ManifestParser( _localizedTextService = localizedTextService; _shortStringHelper = shortStringHelper; _dataValueEditorFactory = dataValueEditorFactory; + _manifestFileProviderFactory = manifestFileProviderFactory; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public ManifestParser( + AppCaches appCaches, + ManifestValueValidatorCollection validators, + ManifestFilterCollection filters, + ILogger logger, + IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, + IJsonSerializer jsonSerializer, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IDataValueEditorFactory dataValueEditorFactory) + : this( + appCaches, + validators, + filters, + logger, + ioHelper, + hostingEnvironment, + jsonSerializer, + localizedTextService, + shortStringHelper, + dataValueEditorFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { } public string AppPluginsPath @@ -89,12 +123,20 @@ public CompositePackageManifest CombinedManifest public IEnumerable GetManifests() { var manifests = new List(); + IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); + + if (manifestFileProvider is null) + { + throw new ArgumentNullException(nameof(manifestFileProvider)); + } - foreach (var path in GetManifestFiles()) + foreach (IFileInfo file in GetManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins)) { try { - var text = File.ReadAllText(path); + using Stream stream = file.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var text = reader.ReadToEnd(); text = TrimPreamble(text); if (string.IsNullOrWhiteSpace(text)) { @@ -102,12 +144,12 @@ public IEnumerable GetManifests() } PackageManifest manifest = ParseManifest(text); - manifest.Source = path; + manifest.Source = file.PhysicalPath!; // We assure that the PhysicalPath is not null in GetManifestFiles() manifests.Add(manifest); } catch (Exception e) { - _logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", path); + _logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", file.PhysicalPath); } } @@ -242,14 +284,44 @@ private static string TrimPreamble(string text) return text; } - // gets all manifest files (recursively) - private IEnumerable GetManifestFiles() + // Gets all manifest files + private static IEnumerable GetManifestFiles(IFileProvider fileProvider, string path) { - if (Directory.Exists(_path) == false) + var manifestFiles = new List(); + IEnumerable pluginFolders = fileProvider.GetDirectoryContents(path); + + foreach (IFileInfo pluginFolder in pluginFolders) { - return Array.Empty(); + if (!pluginFolder.IsDirectory) + { + continue; + } + + manifestFiles.AddRange(GetNestedManifestFiles(fileProvider, $"{path}/{pluginFolder.Name}")); } - return Directory.GetFiles(_path, "package.manifest", SearchOption.AllDirectories); + return manifestFiles; + } + + // Helper method to get all nested package.manifest files (recursively) + private static IEnumerable GetNestedManifestFiles(IFileProvider fileProvider, string path) + { + foreach (IFileInfo file in fileProvider.GetDirectoryContents(path)) + { + if (file.IsDirectory) + { + var virtualPath = WebPath.Combine(path, file.Name); + + // Recursively find nested package.manifest files + foreach (IFileInfo nested in GetNestedManifestFiles(fileProvider, virtualPath)) + { + yield return nested; + } + } + else if (file.Name.InvariantEquals("package.manifest") && !string.IsNullOrEmpty(file.PhysicalPath)) + { + yield return file; + } + } } } diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 19ac4606d1b7..6b922545bb0a 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -14,7 +14,7 @@ - + @@ -25,7 +25,7 @@ - + diff --git a/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs b/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs new file mode 100644 index 000000000000..2fdc54e0118a --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs @@ -0,0 +1,23 @@ +namespace Umbraco.New.Cms.Core; + +// TODO: move this class to Umbraco.Cms.Core as a partial class +public static class Constants +{ + public static partial class OauthClientIds + { + /// + /// Client ID used for default back-office access + /// + public const string BackOffice = "umbraco-back-office"; + + /// + /// Client ID used for Swagger API access + /// + public const string Swagger = "umbraco-swagger"; + + /// + /// Client ID used for Postman API access + /// + public const string Postman = "umbraco-postman"; + } +} diff --git a/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs new file mode 100644 index 000000000000..cad7b8868de7 --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs @@ -0,0 +1,10 @@ +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.New.Cms.Core.Models.Configuration; + +// TODO: merge this class with relevant existing settings from Core and clean up +[UmbracoOptions($"{Umbraco.Cms.Core.Constants.Configuration.ConfigPrefix}NewBackOffice")] +public class NewBackOfficeSettings +{ + public Uri? BackOfficeHost { get; set; } = null; +} diff --git a/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs new file mode 100644 index 000000000000..bb1a2eda3d69 --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models.Validation; + +namespace Umbraco.New.Cms.Core.Models.Configuration; + +// TODO: merge this class with relevant existing settings validators from Core and clean up +public class NewBackOfficeSettingsValidator : ConfigurationValidatorBase, IValidateOptions +{ + public ValidateOptionsResult Validate(string? name, NewBackOfficeSettings options) + { + if (options.BackOfficeHost != null) + { + if (options.BackOfficeHost.IsAbsoluteUri == false) + { + return ValidateOptionsResult.Fail($"{nameof(NewBackOfficeSettings.BackOfficeHost)} must be an absolute URL"); + } + if (options.BackOfficeHost.PathAndQuery != "/") + { + return ValidateOptionsResult.Fail($"{nameof(NewBackOfficeSettings.BackOfficeHost)} must not have any path or query"); + } + } + + return ValidateOptionsResult.Success; + } +} diff --git a/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs b/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs new file mode 100644 index 000000000000..37a6e4caa633 --- /dev/null +++ b/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs @@ -0,0 +1,56 @@ +using System.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenIddict.Abstractions; +using Umbraco.Cms.Infrastructure.HostedServices; + +namespace Umbraco.New.Cms.Infrastructure.HostedServices; + +// port of the OpenIddict Quartz job for cleaning up - see https://github.com/openiddict/openiddict-core/tree/dev/src/OpenIddict.Quartz +public class OpenIddictCleanup : RecurringHostedServiceBase +{ + // keep tokens and authorizations in the database for 7 days + // - NOTE: this is NOT the same as access token lifetime, which is likely very short + private const int LifespanInSeconds = 7 * 24 * 60 * 60; + + private readonly ILogger _logger; + private readonly IServiceProvider _provider; + + public OpenIddictCleanup( + ILogger logger, IServiceProvider provider) + : base(logger, TimeSpan.FromHours(1), TimeSpan.FromMinutes(5)) + { + _logger = logger; + _provider = provider; + } + + public override async Task PerformExecuteAsync(object? state) + { + // hosted services are registered as singletons, but this particular one consumes scoped services... so + // we have to fetch the service dependencies manually using a new scope per invocation. + IServiceScope scope = _provider.CreateScope(); + DateTimeOffset threshold = DateTimeOffset.UtcNow - TimeSpan.FromSeconds(LifespanInSeconds); + + try + { + IOpenIddictTokenManager tokenManager = scope.ServiceProvider.GetService() + ?? throw new ConfigurationErrorsException($"Could not retrieve an {nameof(IOpenIddictTokenManager)} service from the current scope"); + await tokenManager.PruneAsync(threshold); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to prune OpenIddict tokens"); + } + + try + { + IOpenIddictAuthorizationManager authorizationManager = scope.ServiceProvider.GetService() + ?? throw new ConfigurationErrorsException($"Could not retrieve an {nameof(IOpenIddictAuthorizationManager)} service from the current scope"); + await authorizationManager.PruneAsync(threshold); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to prune OpenIddict authorizations"); + } + } +} diff --git a/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs b/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs new file mode 100644 index 000000000000..068f5df472f5 --- /dev/null +++ b/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs @@ -0,0 +1,6 @@ +namespace Umbraco.New.Cms.Infrastructure.Security; + +public interface IBackOfficeApplicationManager +{ + Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default); +} diff --git a/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj b/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj index 8dc5d3cc0034..82159079a4ee 100644 --- a/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj +++ b/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs index ae31876ac519..4d708b4fc857 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs @@ -1,16 +1,21 @@ using System.Text; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Tour; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Web.BackOffice.Controllers; @@ -20,22 +25,44 @@ public class TourController : UmbracoAuthorizedJsonController private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; private readonly IContentTypeService _contentTypeService; private readonly TourFilterCollection _filters; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IWebHostEnvironment _webHostEnvironment; private readonly TourSettings _tourSettings; + // IHostingEnvironment is still injected as when removing it, the number of + // parameters matches with the obsolete ctor and the two ctors become ambiguous + // [ActivatorUtilitiesConstructor] won't solve the problem in this case + // IHostingEnvironment can be removed when the obsolete ctor is removed + [ActivatorUtilitiesConstructor] public TourController( TourFilterCollection filters, IHostingEnvironment hostingEnvironment, IOptionsSnapshot tourSettings, IBackOfficeSecurityAccessor backofficeSecurityAccessor, - IContentTypeService contentTypeService) + IContentTypeService contentTypeService, + IWebHostEnvironment webHostEnvironment) { _filters = filters; - _hostingEnvironment = hostingEnvironment; - _tourSettings = tourSettings.Value; _backofficeSecurityAccessor = backofficeSecurityAccessor; _contentTypeService = contentTypeService; + _webHostEnvironment = webHostEnvironment; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public TourController( + TourFilterCollection filters, + IHostingEnvironment hostingEnvironment, + IOptionsSnapshot tourSettings, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IContentTypeService contentTypeService) + : this( + filters, + hostingEnvironment, + tourSettings, + backofficeSecurityAccessor, + contentTypeService, + StaticServiceProvider.Instance.GetRequiredService()) + { } public async Task> GetTours() @@ -53,69 +80,55 @@ public async Task> GetTours() return result; } - //get all filters that will be applied to all tour aliases + // Get all filters that will be applied to all tour aliases var aliasOnlyFilters = _filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); - //don't pass in any filters for core tours that have a plugin name assigned + // Don't pass in any filters for core tours that have a plugin name assigned var nonPluginFilters = _filters.Where(x => x.PluginName == null).ToList(); - //add core tour files - IEnumerable embeddedTourNames = GetType() - .Assembly - .GetManifestResourceNames() - .Where(x => x.StartsWith("Umbraco.Cms.Web.BackOffice.EmbeddedResources.Tours.")); - foreach (var embeddedTourName in embeddedTourNames) + // Get core tour files + IFileProvider toursProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Web.BackOffice.EmbeddedResources.Tours"); + + IEnumerable embeddedTourFiles = toursProvider.GetDirectoryContents(string.Empty) + .Where(x => !x.IsDirectory && x.Name.EndsWith(".json")); + + foreach (var embeddedTour in embeddedTourFiles) { - await TryParseTourFile(embeddedTourName, result, nonPluginFilters, aliasOnlyFilters, async x => await GetContentFromEmbeddedResource(x)); + using Stream stream = embeddedTour.CreateReadStream(); + await TryParseTourFile(embeddedTour.Name, result, nonPluginFilters, aliasOnlyFilters, stream); } + // Collect all tour files from packages - /App_Plugins physical or virtual locations + IEnumerable> toursFromPackages = GetTourFiles(_webHostEnvironment.WebRootFileProvider, Constants.SystemDirectories.AppPlugins); - //collect all tour files in packages - var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins); - if (Directory.Exists(appPlugins)) + foreach (var tuple in toursFromPackages) { - foreach (var plugin in Directory.EnumerateDirectories(appPlugins)) - { - var pluginName = Path.GetFileName(plugin.TrimEnd(Constants.CharArrays.Backslash)); - var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)) - .ToList(); - - //If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely - var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); - if (isPluginFiltered) - { - continue; - } + string pluginName = tuple.Item2; + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList(); - //combine matched package filters with filters not specific to a package - var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); + // Combine matched package filters with filters not specific to a package + var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); - foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice")) - { - foreach (var tourDir in Directory.EnumerateDirectories(backofficeDir, "tours")) - { - foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json")) - { - await TryParseTourFile( - tourFile, - result, - combinedFilters, - aliasOnlyFilters, - async x => await System.IO.File.ReadAllTextAsync(x), - pluginName); - } - } - } + IFileInfo tourFile = tuple.Item1; + using (Stream stream = tourFile.CreateReadStream()) + { + await TryParseTourFile( + tourFile.Name, + result, + combinedFilters, + aliasOnlyFilters, + stream, + pluginName); } } - //Get all allowed sections for the current user + // Get all allowed sections for the current user var allowedSections = user.AllowedSections.ToList(); var toursToBeRemoved = new List(); - //Checking to see if the user has access to the required tour sections, else we remove the tour + // Checking to see if the user has access to the required tour sections, else we remove the tour foreach (BackOfficeTourFile backOfficeTourFile in result) { if (backOfficeTourFile.Tours != null) @@ -140,21 +153,70 @@ await TryParseTourFile( return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); } - private async Task GetContentFromEmbeddedResource(string fileName) + private IEnumerable> GetTourFiles(IFileProvider fileProvider, string folder) { - Stream? resourceStream = GetType().Assembly.GetManifestResourceStream(fileName); + IEnumerable pluginFolders = fileProvider.GetDirectoryContents(folder).Where(x => x.IsDirectory); - if (resourceStream is null) + foreach (IFileInfo pluginFolder in pluginFolders) { - return string.Empty; + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginFolder.Name)).ToList(); + + // If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely + var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); + if (isPluginFiltered) + { + continue; + } + + // get the full virtual path for the plugin folder + var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name); + + // loop through the folder(s) in order to find tours + // - there could be multiple on case sensitive file system + // Hard-coding the "backoffice" directory name to gain a better performance when traversing the App_Plugins directory + foreach (var subDir in GetToursFolderPaths(fileProvider, pluginFolderPath, "backoffice")) + { + IEnumerable tourFiles = fileProvider + .GetDirectoryContents(subDir) + .Where(x => x.Name.InvariantEndsWith(".json")); + + foreach (IFileInfo file in tourFiles) + { + yield return new Tuple(file, pluginFolder.Name); + } + } } + } - using var reader = new StreamReader(resourceStream, Encoding.UTF8); - return await reader.ReadToEndAsync(); + private static IEnumerable GetToursFolderPaths(IFileProvider fileProvider, string path, string subDirName) + { + // Hard-coding the "tours" directory name to gain a better performance when traversing the sub directories + const string toursDirName = "tours"; + + // It is necessary to iterate through the subfolders because on Linux we'll get casing issues when + // we try to access {path}/{pluginDirectory.Name}/backoffice/tours directly + foreach (IFileInfo subDir in fileProvider.GetDirectoryContents(path)) + { + // InvariantEquals({dirName}) is used to gain a better performance when looking for the tours folder + if (subDir.IsDirectory && subDir.Name.InvariantEquals(subDirName)) + { + var virtualPath = WebPath.Combine(path, subDir.Name); + + if (subDir.Name.InvariantEquals(toursDirName)) + { + yield return virtualPath; + } + + foreach (var nested in GetToursFolderPaths(fileProvider, virtualPath, toursDirName)) + { + yield return nested; + } + } + } } /// - /// Gets a tours for a specific doctype + /// Gets a tours for a specific doctype. /// /// The documenttype alias /// A @@ -190,7 +252,7 @@ private async Task TryParseTourFile( ICollection result, List filters, List aliasOnlyFilters, - Func> fileNameToFileContent, + Stream fileStream, string? pluginName = null) { var fileName = Path.GetFileNameWithoutExtension(tourFile); @@ -199,24 +261,25 @@ private async Task TryParseTourFile( return; } - //get the filters specific to this file + // Get the filters specific to this file var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList(); - //If there is any filter applied to match the file only (no tour alias) then ignore the file entirely + // If there is any filter applied to match the file only (no tour alias) then ignore the file entirely var isFileFiltered = fileFilters.Any(x => x.TourAlias == null); if (isFileFiltered) { return; } - //now combine all aliases to filter below + // Now combine all aliases to filter below var aliasFilters = aliasOnlyFilters.Concat(filters.Where(x => x.TourAlias != null)) .Select(x => x.TourAlias) .ToList(); try { - var contents = await fileNameToFileContent(tourFile); + using var reader = new StreamReader(fileStream, Encoding.UTF8); + var contents = reader.ReadToEnd(); BackOfficeTour[]? tours = JsonConvert.DeserializeObject(contents); IEnumerable? backOfficeTours = tours?.Where(x => @@ -234,7 +297,7 @@ private async Task TryParseTourFile( Tours = localizedTours ?? new List() }; - //don't add if all of the tours are filtered + // Don't add if all of the tours are filtered if (tour.Tours.Any()) { result.Add(tour); diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs index 60a946a553d0..7a92a77b3c37 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs @@ -1,8 +1,6 @@ -using System.IO; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -31,7 +29,7 @@ private static IUmbracoBuilder AddSupplemenataryLocalizedTextFileSources(this IU /// - /// Loads the suplimentary localization files from plugins and user config + /// Loads the suplimentary localization files from plugins and user config. /// private static IEnumerable GetSupplementaryFileSources( IWebHostEnvironment webHostEnvironment) @@ -41,10 +39,10 @@ private static IEnumerable GetSuppl IEnumerable localPluginFileSources = GetPluginLanguageFileSources(contentFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false); - // gets all langs files in /app_plugins real or virtual locations + // Gets all language files in /app_plugins real or virtual locations IEnumerable pluginLangFileSources = GetPluginLanguageFileSources(webFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false); - // user defined language files that overwrite the default, these should not be used by plugin creators + // User defined language files that overwrite the default, these should not be used by plugin creators var userConfigLangFolder = Cms.Core.Constants.SystemDirectories.Config .TrimStart(Cms.Core.Constants.CharArrays.Tilde); @@ -56,7 +54,7 @@ private static IEnumerable GetSuppl { foreach (IFileInfo langFile in contentFileProvider.GetDirectoryContents($"{userConfigLangFolder}/{langFileSource.Name}")) { - if (langFile.Name.InvariantEndsWith(".xml") && langFile.PhysicalPath is not null) + if (langFile.Name.InvariantEndsWith(".xml")) { configLangFileSources.Add(new LocalizedTextServiceSupplementaryFileSource(langFile, true)); } @@ -64,10 +62,9 @@ private static IEnumerable GetSuppl } } - return - localPluginFileSources - .Concat(pluginLangFileSources) - .Concat(configLangFileSources); + return localPluginFileSources + .Concat(pluginLangFileSources) + .Concat(configLangFileSources); } @@ -75,7 +72,7 @@ private static IEnumerable GetSuppl /// Loads the suplimentary localaization files via the file provider. /// /// - /// locates all *.xml files in the lang folder of any sub folder of the one provided. + /// Locates all *.xml files in the lang folder of any sub folder of the one provided. /// e.g /app_plugins/plugin-name/lang/*.xml /// private static IEnumerable GetPluginLanguageFileSources( @@ -87,17 +84,16 @@ private static IEnumerable GetPlugi foreach (IFileInfo pluginFolder in pluginFolders) { - // get the full virtual path for the plugin folder + // Get the full virtual path for the plugin folder var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name); - // loop through the lang folder(s) + // Loop through the lang folder(s) // - there could be multiple on case sensitive file system foreach (var langFolder in GetLangFolderPaths(fileProvider, pluginFolderPath)) { - // request all the files out of the path, these will have physicalPath set. + // Request all the files out of the path IEnumerable localizationFiles = fileProvider .GetDirectoryContents(langFolder) - .Where(x => !string.IsNullOrEmpty(x.PhysicalPath)) .Where(x => x.Name.InvariantEndsWith(".xml")); foreach (IFileInfo file in localizationFiles) diff --git a/src/Umbraco.Web.BackOffice/Services/IconService.cs b/src/Umbraco.Web.BackOffice/Services/IconService.cs index ac7ae2455e42..068db52be669 100644 --- a/src/Umbraco.Web.BackOffice/Services/IconService.cs +++ b/src/Umbraco.Web.BackOffice/Services/IconService.cs @@ -34,7 +34,6 @@ public IconService( { } - [Obsolete("Use other ctor - Will be removed in Umbraco 12")] public IconService( IOptionsMonitor globalSettings, IHostingEnvironment hostingEnvironment, @@ -101,6 +100,7 @@ public IconService( } } + // TODO: Refactor to return IEnumerable private IEnumerable GetAllIconsFiles() { var icons = new HashSet(new CaseInsensitiveFileInfoComparer()); @@ -161,46 +161,45 @@ private IEnumerable GetAllIconsFiles() /// A collection of representing the found SVG icon files. private static IEnumerable GetIconsFiles(IFileProvider fileProvider, string path) { - // Iterate through all plugin folders, this is necessary because on Linux we'll get casing issues when + // Iterate through all plugin folders and their subfolders, this is necessary because on Linux we'll get casing issues when // we directly try to access {path}/{pluginDirectory.Name}/{Constants.SystemDirectories.PluginIcons} foreach (IFileInfo pluginDirectory in fileProvider.GetDirectoryContents(path)) { - // Ideally there shouldn't be any files, but we'd better check to be sure if (!pluginDirectory.IsDirectory) { continue; } - // Iterate through the sub directories of each plugin folder + // Iterate through the sub directories of each plugin folder in order to support case insensitive paths (for Linux) foreach (IFileInfo subDir1 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}")) { - // Skip files again - if (!subDir1.IsDirectory) - { - continue; - } - - // Iterate through second level sub directories - foreach (IFileInfo subDir2 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}")) + // Hard-coding the "backoffice" directory name to gain a better performance when traversing the pluginDirectory directories + if (subDir1.IsDirectory && subDir1.Name.InvariantEquals("backoffice")) { - // Skip files again - if (!subDir2.IsDirectory) + // Iterate through second level sub directories in order to support case insensitive paths (for Linux) + foreach (IFileInfo subDir2 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}")) { - continue; - } + if (!subDir2.IsDirectory) + { + continue; + } - // Does the directory match the plugin icons folder? (case insensitive for legacy support) - if (!$"/{subDir1.Name}/{subDir2.Name}".InvariantEquals(Constants.SystemDirectories.PluginIcons)) - { - continue; - } + // Does the directory match the plugin icons folder? (case insensitive for legacy support) + if (!$"/{subDir1.Name}/{subDir2.Name}".InvariantEquals(Constants.SystemDirectories.PluginIcons)) + { + continue; + } - // Iterate though the files of the second level sub directory. This should be where the SVG files are located :D - foreach (IFileInfo file in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}/{subDir2.Name}")) - { - if (file.Name.InvariantEndsWith(".svg") && file.PhysicalPath != null) + // Iterate though the files of the second level sub directory. This should be where the SVG files are located :D + foreach (IFileInfo file in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}/{subDir2.Name}")) { - yield return new FileInfo(file.PhysicalPath); + // TODO: Refactor to work with IFileInfo, then we can also remove the .PhysicalPath check + // as this won't work for files that aren't located on a physical file system + // (e.g. embedded resource, Azure Blob Storage, etc.) + if (file.Name.InvariantEndsWith(".svg") && !string.IsNullOrEmpty(file.PhysicalPath)) + { + yield return new FileInfo(file.PhysicalPath); + } } } } diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs index af49c43e9047..275e28d9aab2 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs @@ -20,8 +20,7 @@ public class MemberGroupTreeController : MemberTypeAndGroupTreeControllerBase { private readonly IMemberGroupService _memberGroupService; - [ - ActivatorUtilitiesConstructor] + [ActivatorUtilitiesConstructor] public MemberGroupTreeController( ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 500bc2244759..edfe999d206f 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -27,6 +26,7 @@ using Umbraco.Cms.Core.Diagnostics; using Umbraco.Cms.Core.Extensions; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Net; @@ -48,6 +48,7 @@ using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Common.DependencyInjection; +using Umbraco.Cms.Web.Common.FileProviders; using Umbraco.Cms.Web.Common.Localization; using Umbraco.Cms.Web.Common.Macros; using Umbraco.Cms.Web.Common.Middleware; @@ -146,6 +147,11 @@ public static IUmbracoBuilder AddUmbracoCore(this IUmbracoBuilder builder) builder.Services.TryAddEnumerable(ServiceDescriptor .Singleton()); + // WebRootFileProviderFactory is just a wrapper around the IWebHostEnvironment.WebRootFileProvider, + // therefore no need to register it as singleton + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + // Must be added here because DbProviderFactories is netstandard 2.1 so cannot exist in Infra for now builder.Services.AddSingleton(factory => new DbProviderFactoryCreator( DbProviderFactories.GetFactory, diff --git a/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs new file mode 100644 index 000000000000..64824dd0906d --- /dev/null +++ b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Web.Common.FileProviders; + +public class WebRootFileProviderFactory : IManifestFileProviderFactory, IGridEditorsConfigFileProviderFactory +{ + private readonly IWebHostEnvironment _webHostEnvironment; + + /// + /// Initializes a new instance of the class. + /// + /// The web hosting environment an application is running in. + public WebRootFileProviderFactory(IWebHostEnvironment webHostEnvironment) + { + _webHostEnvironment = webHostEnvironment; + } + + /// + /// Creates a new instance, pointing at . + /// + /// + /// The newly created instance. + /// + public IFileProvider Create() => _webHostEnvironment.WebRootFileProvider; +} diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 7d5bc5ca743d..00feb6d8b6a6 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js index 5827f7e5306c..13ca4cb1930b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js @@ -11,7 +11,11 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events //check if there's a returnPath query string, if so redirect to it var locationObj = $location.search(); if (locationObj.returnPath) { - path = decodeURIComponent(locationObj.returnPath); + // decodeURIComponent(...) does not play nice with OAuth redirect URLs, so until we have a + // dedicated login screen for the new back-office, we need to hardcode this exception + path = locationObj.returnPath.indexOf("/security/back-office/authorize") > 0 + ? locationObj.returnPath + : decodeURIComponent(locationObj.returnPath); } // Ensure path is not absolute diff --git a/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs b/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs index 26cc13949c7e..2d387dcdff79 100644 --- a/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs +++ b/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs @@ -78,6 +78,7 @@ public void CloseSharedConnection() public DbTransaction Transaction { get; } public IDictionary Data { get; } + public int CommandTimeout { get; set; } public ISqlContext SqlContext { get; } diff --git a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs index 9178234e7433..c4c0bad3a966 100644 --- a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs +++ b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs @@ -1,15 +1,11 @@ -using System; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Tests.Integration.TestServerTest; -using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration.NewBackoffice; @@ -39,7 +35,7 @@ public async Task Validate_OpenApi_Contract_is_implemented() var officePath = GlobalSettings.GetBackOfficePath(HostingEnvironment); var urlToContract = $"{officePath}/management/api/openapi.json"; - var swaggerPath = $"{officePath}/swagger/All/swagger.json"; + var swaggerPath = $"{officePath}/swagger/v1/swagger.json"; var apiContract = JObject.Parse(await Client.GetStringAsync(urlToContract)); var generatedJsonString = await Client.GetStringAsync(swaggerPath); diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 5babe9aa0b89..a174476acd3e 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -9,7 +7,6 @@ using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -22,8 +19,6 @@ using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Integration.DependencyInjection; using Umbraco.Cms.Tests.Integration.Extensions; -using Umbraco.Cms.Web.Common.Hosting; -using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.Testing; diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs index 94a64b31c91c..2099d1d53702 100644 --- a/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs +++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs @@ -23,7 +23,8 @@ public static IUmbracoContextFactory Create( GlobalSettings globalSettings = null, IUmbracoContextAccessor umbracoContextAccessor = null, IHttpContextAccessor httpContextAccessor = null, - IPublishedUrlProvider publishedUrlProvider = null) + IPublishedUrlProvider publishedUrlProvider = null, + UmbracoRequestPathsOptions umbracoRequestPathsOptions = null) { if (globalSettings == null) { @@ -45,6 +46,11 @@ public static IUmbracoContextFactory Create( publishedUrlProvider = Mock.Of(); } + if (umbracoRequestPathsOptions == null) + { + umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); + } + var contentCache = new Mock(); var mediaCache = new Mock(); var snapshot = new Mock(); @@ -58,7 +64,7 @@ public static IUmbracoContextFactory Create( var umbracoContextFactory = new UmbracoContextFactory( umbracoContextAccessor, snapshotService.Object, - new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment), + new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment, Options.Create(umbracoRequestPathsOptions)), hostingEnvironment, new UriUtility(hostingEnvironment), new AspNetCoreCookieManager(httpContextAccessor), diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs index 1596bf988884..3cc54f49331b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs @@ -307,9 +307,9 @@ public void KnownTypes() [UdiDefinition("foo", UdiType.GuidUdi)] public class FooConnector : IServiceConnector { - public IArtifact GetArtifact(Udi udi) => throw new NotImplementedException(); + public IArtifact GetArtifact(Udi udi, IContextCache contextCache) => throw new NotImplementedException(); - public IArtifact GetArtifact(object entity) => throw new NotImplementedException(); + public IArtifact GetArtifact(object entity, IContextCache contextCache) => throw new NotImplementedException(); public ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context) => throw new NotImplementedException(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs index 03f635b2cd3b..71d1a25228c4 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs @@ -47,7 +47,8 @@ public void Setup() new JsonNetSerializer(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of()); } private ManifestParser _parser; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs index 744febae67a5..4ed6ce842ca0 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs @@ -18,10 +18,12 @@ public void Setup() { _hostEnvironment = Mock.Of(); _globalSettings = new GlobalSettings(); + _umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); } private IWebHostEnvironment _hostEnvironment; private GlobalSettings _globalSettings; + private UmbracoRequestPathsOptions _umbracoRequestPathsOptions; private IHostingEnvironment CreateHostingEnvironment(string virtualPath = "") { @@ -49,7 +51,7 @@ private IHostingEnvironment CreateHostingEnvironment(string virtualPath = "") public void Is_Client_Side_Request(string url, bool assert) { var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); var uri = new Uri("http://test.com" + url); var result = umbracoRequestPaths.IsClientSideRequest(uri.AbsolutePath); @@ -60,7 +62,7 @@ public void Is_Client_Side_Request(string url, bool assert) public void Is_Client_Side_Request_InvalidPath_ReturnFalse() { var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); // This URL is invalid. Default to false when the extension cannot be determined var uri = new Uri("http://test.com/installing-modules+foobar+\"yipee\""); @@ -85,11 +87,13 @@ public void Is_Client_Side_Request_InvalidPath_ReturnFalse() [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)] [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)] [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)] + // NOTE: this test case is false for now - will be true once the IsBackOfficeRequest tweak from the new management API is put into UmbracoRequestPaths + [TestCase("http://www.domain.com/umbraco/management/api/v1.0/my/controller/action/", "", false)] public void Is_Back_Office_Request(string input, string virtualPath, bool expected) { var source = new Uri(input); var hostingEnvironment = CreateHostingEnvironment(virtualPath); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath)); } @@ -106,7 +110,21 @@ public void Is_Installer_Request(string input, bool expected) { var source = new Uri(input); var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); Assert.AreEqual(expected, umbracoRequestPaths.IsInstallerRequest(source.AbsolutePath)); } + + [TestCase("http://www.domain.com/some/path", false)] + [TestCase("http://www.domain.com/umbraco/surface/blah", false)] + [TestCase("http://www.domain.com/umbraco/api/blah", false)] + [TestCase("http://www.domain.com/umbraco/management/api/v1.0/my/controller/action/", true)] + public void Force_Back_Office_Request_With_Request_Paths_Options(string input, bool expected) + { + var source = new Uri(input); + var hostingEnvironment = CreateHostingEnvironment(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); + umbracoRequestPathsOptions.IsBackOfficeRequest = _ => true; + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(umbracoRequestPathsOptions)); + Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath)); + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs index b1881c132efe..c59f9db68fe0 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs @@ -24,12 +24,13 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_When_Not_Configured() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( Mock.Of(), runtime, - new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment()), + new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment(), Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/umbraco"); @@ -41,6 +42,7 @@ public void ShouldAuthenticateRequest_When_Not_Configured() public void ShouldAuthenticateRequest_When_Configured() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager( @@ -49,7 +51,8 @@ public void ShouldAuthenticateRequest_When_Configured() new UmbracoRequestPaths( Options.Create(globalSettings), Mock.Of(x => - x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco")), + x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/umbraco"); @@ -61,6 +64,7 @@ public void ShouldAuthenticateRequest_When_Configured() public void ShouldAuthenticateRequest_Is_Back_Office() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); @@ -73,7 +77,8 @@ public void ShouldAuthenticateRequest_Is_Back_Office() Options.Create(globalSettings), Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && - x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")), + x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest(remainingTimeoutSecondsPath); @@ -87,6 +92,7 @@ public void ShouldAuthenticateRequest_Is_Back_Office() public void ShouldAuthenticateRequest_Not_Back_Office() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); @@ -97,7 +103,8 @@ public void ShouldAuthenticateRequest_Not_Back_Office() Options.Create(globalSettings), Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && - x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")), + x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/notbackoffice"); diff --git a/version.json b/version.json index 579911bae39b..e8639110f48a 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": "11.0.0-rc1", + "version": "11.1.0-rc", "assemblyVersion": { "precision": "build" },