diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b2e6ac484f..c14b41f13f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -61,7 +61,7 @@ This guide describes each step to make your first contribution: ## Further contribution guides - [Before you start](contributing-before-you-start.md) -- [Finding your first issue: Up for grabs](contributing-before-you-start.md) +- [Finding your first issue: Up for grabs](contributing-first-issue.md) - [Contributing to the new backoffice](https://docs.umbraco.com/umbraco-backoffice/) - [Unwanted changes](contributing-unwanted-changes.md) - [Other ways to contribute](contributing-other-ways-to-contribute.md) diff --git a/.github/workflows/azure-backoffice.yml b/.github/workflows/azure-backoffice.yml new file mode 100644 index 0000000000..46597a42fd --- /dev/null +++ b/.github/workflows/azure-backoffice.yml @@ -0,0 +1,55 @@ +name: Backoffice Static Web Apps CI/CD + +on: + push: + branches: + - contrib + - v*/dev + paths: + - src/Umbraco.Web.UI.Client/package.json + - src/Umbraco.Web.UI.Client/package-lock.json + - src/Umbraco.Web.UI.Client/src/** + - .github/workflows/azure-backoffice.yml + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - contrib + - v*/dev + +jobs: + build_and_deploy_job: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed' && (contains(github.event.pull_request.labels.*.name, 'preview/backoffice'))) + runs-on: ubuntu-latest + name: Build and Deploy Job + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Build And Deploy + id: builddeploy + uses: Azure/static-web-apps-deploy@v1 + with: + production_branch: contrib + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_VICTORIOUS_GROUND_017B08103 }} + repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) + action: "upload" + ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: "src/Umbraco.Web.UI.Client" # App source code path + app_build_command: "npm run build:for:static" + output_location: "dist" # Built app content directory - optional + skip_api_build: true # Set to true if you do not have an Azure Functions API in your repo + ###### End of Repository/Build Configurations ###### + + close_pull_request_job: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + app_location: "src/Umbraco.Web.UI.Client" + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_VICTORIOUS_GROUND_017B08103 }} + action: "close" diff --git a/.github/workflows/azure-storybook.yml b/.github/workflows/azure-storybook.yml new file mode 100644 index 0000000000..6702a443cb --- /dev/null +++ b/.github/workflows/azure-storybook.yml @@ -0,0 +1,56 @@ +name: Storybook CI/CD + +on: + push: + branches: + - contrib + - v*/dev + paths: + - src/Umbraco.Web.UI.Client/package.json + - src/Umbraco.Web.UI.Client/package-lock.json + - src/Umbraco.Web.UI.Client/src/** + - .github/workflows/azure-storybook.yml + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - contrib + - v*/dev + +env: + NODE_OPTIONS: --max_old_space_size=16384 + +jobs: + build_and_deploy_job: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed' && (contains(github.event.pull_request.labels.*.name, 'preview/storybook'))) + runs-on: ubuntu-latest + name: Build and Deploy Job + steps: + - uses: actions/checkout@v4 + - name: Build And Deploy + id: builddeploy + uses: Azure/static-web-apps-deploy@v1 + with: + production_branch: contrib + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ORANGE_SEA_0C7411A03 }} + repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) + action: "upload" + ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: "src/Umbraco.Web.UI.Client" # App source code path + app_build_command: "npm run storybook:build" + output_location: "/storybook-static" # Built app content directory - optional + skip_api_build: true # Set to true if you do not have an Azure Functions API in your repo + ###### End of Repository/Build Configurations ###### + + close_pull_request_job: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + app_location: "src/Umbraco.Web.UI.Client" + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ORANGE_SEA_0C7411A03 }} + action: "close" diff --git a/.vscode/settings.json b/.vscode/settings.json index 3119ae6509..6d4441fffc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { - "cSpell.words": [ - "unprovide" - ] + "cSpell.words": ["unprovide"], + "eslint.useFlatConfig": true, + "eslint.workingDirectories": [ + "./src/Umbraco.Web.UI.Client/", + "./src/Umbraco.Web.UI.Login/" + ] } diff --git a/Directory.Build.props b/Directory.Build.props index 7e8128fd83..4dda16251e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,6 +20,7 @@ false + false - true + false 15.0.0 true true diff --git a/NOTICES.txt b/NOTICES.txt new file mode 100644 index 0000000000..1ea114b715 --- /dev/null +++ b/NOTICES.txt @@ -0,0 +1,571 @@ +Third-Party Notices +=================== + +This file contains notices and attributions for third-party software used in the Umbraco CMS project. + +It is not a license and does not grant any rights to use the third-party software. + +Umbraco CMS is licensed under the MIT License, which can be found in the LICENSE file. + +--- + +@openid/AppAuth-JS: An OpenID Connect and OAuth 2.0 client library for JavaScript + +URL: https://github.com/openid/AppAuth-JS +License: Apache License, Version 2.0 +Copyright: 2017 Google Inc. + +--- + +AutoFixture: Write maintainable unit tests, faster + +URL: https://github.com/AutoFixture/AutoFixture +License: MIT License +Copyright: 2013 Mark Seemann + +--- + +Asp.Versioning.Mvc: A library for ASP.NET Core versioning + +URL: https://github.com/dotnet/aspnet-api-versioning +License: MIT License +Copyright: .NET Foundation and contributors + +--- + +Babel: A JavaScript compiler + +URL: https://babeljs.io/ +License: MIT License +Copyright: 2014-present Sebastian McKenzie and other contributors + +--- + +BenchmarkDotNet: Powerful .NET library for benchmarking + +URL: https://github.com/dotnet/BenchmarkDotNet +License: MIT License +Copyright: .NET Foundation and Contributors + +--- + +Bogus: A simple and sane data generator for populating objects that supports different locales. + +URL: https://github.com/bchavez/Bogus +License: MIT License +Copyright: 2015 Brian Chavez + +--- + +CommandLineParser: Terse syntax C# command line parser for .NET + +URL: https://github.com/commandlineparser/commandline +License: MIT License +Copyright: 2005-2015 Giacomo Stelluti Scala & Contributors + +--- + +cross-env: A CLI tool to set environment variables across platforms + +URL: https://github.com/kentcdodds/cross-env +License: MIT License +Copyright: 2017 Kent C. Dodds + +--- + +Dazinator.Extensions.FileProviders: A library for file provider extensions + +URL: https://github.com/dazinator/Dazinator.Extensions.FileProviders +License: MIT License +Copyright: 2016 Darrell + +--- + +DOMPurify: A DOM-only XSS sanitizer for HTML, MathML and SVG + +URL: https://github.com/cure53/DOMPurify +License: Apache License, Version 2.0 +Copyright: 2025 Dr.-Ing. Mario Heiderich, Cure53 + +--- + +Element Internals Polyfill: A polyfill for the Element Internals API + +URL: https://github.com/calebdwilliams/element-internals-polyfill +License: MIT License +Copyright: 2021 Caleb Williams + +--- + +Eslint: A tool for identifying and reporting on patterns in JavaScript + +URL: https://eslint.org/ +License: MIT License +Copyright: OpenJS Foundation and other contributors + +--- + +Examine: A search and indexing library for .NET + +URL: https://github.com/Shazwazza/Examine +License: Microsoft Public License (Ms-PL) +Copyright: 2023 Shannon Deminick + +--- + +Glob: A library for matching file paths using glob patterns + +URL: https://github.com/isaacs/node-glob +License: ISC License +Copyright: 2009-2023 Isaac Z. Schlueter and Contributors + +--- + +Globals: A library for managing global variables in JavaScript + +URL: https://github.com/sindresorhus/globals +License: MIT License +Copyright: Sindre Sorhus + +--- + +Html Agility Pack: An HTML parser for .NET + +URL: https://html-agility-pack.net/ +License: MIT License +Copyright: ZZZ Projects Inc. + +--- + +ImageSharp: A cross-platform library for processing images in .NET + +URL: https://github.com/SixLabors/ImageSharp +License: Apache License, Version 2.0 under the Six Labors Split License +Copyright: Six Labors + +--- + +jsdiff: A JavaScript text differencing implementation + +URL: https://github.com/kpdecker/jsdiff +License: BSD 3-Clause License +Copyright: 2009-2015 Kevin Decker + +--- + +JsonPatch.Net: A library for JSON Patch (RFC 6902) in .NET + +URL: https://github.com/json-everything/json-everything +License: MIT License +Copyright: .NET Foundation and Contributors + +--- + +K4os.Compression.LZ4: A fast LZ4 compression library for .NET + +URL: https://github.com/MiloszKrajewski/K4os.Compression.LZ4 +License: MIT License +Copyright: 2017 Milosz Krajewski + +--- + +Lit: A simple library for building fast, lightweight web components + +URL: https://lit.dev +License: BSD 3-Clause License +Copyright: 2020 Google LLC. All rights reserved. + +--- + +Lucide: Beautiful & consistent icons for the web + +URL: https://lucide.dev/ +License: ISC License +Copyright: 2013-2022 Cole Bemis +Copyright: 2022 Lucide Contributors + +--- + +Madge: A dependency graph generator for JavaScript + +URL: https://github.com/pahen/madge +License: MIT License +Copyright: 2017 Patrik Henningsson + +--- + +MailKit: A library for sending email in .NET + +URL: https://github.com/jstedfast/MailKit +License: MIT License +Copyright: 2013-2024 .NET Foundation and Contributors + +--- + +Markdown: A library for parsing and compiling Markdown + +URL: https://github.com/hey-red/Markdown +License: MIT License +Copyright: 2018 red + +--- + +marked: A markdown parser and compiler + +URL: https://marked.js.org/ +License: MIT License +Copyright: 2011-2018, Christopher Jeffrey (https://github.com/chjj/) +Copyright: 2018+, MarkedJS (https://github.com/markedjs/) + +--- + +Message Pack: The extremely fast MessagePack serializer for C# + +URL: https://github.com/MessagePack-CSharp/MessagePack-CSharp +License: MIT License +Copyright: 2017 Yoshifumi Kawai and contributors + +--- + +Miniprofiler: A mini profiler for .NET + +URL: https://github.com/MiniProfiler/dotnet +License: MIT License +Copyright: .NET MiniProfiler Contributors + +--- + +Monaco Editor: A browser-based code editor + +URL: https://microsoft.github.io/monaco-editor/ +License: MIT License +Copyright: 2016-present Microsoft Corporation + +--- + +Moq: A mocking library for .NET + +URL: https://github.com/moq/moq +License: BSD 3-Clause License +Copyright: 2007 Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors. + +--- + +Mock Service Worker (MSW): A library for mocking API requests in JavaScript + +URL: https://mswjs.io/ +License: MIT License +Copyright: 2018–present Artem Zakharchenko + +--- + +NCrontab: A cron schedule parser for .NET + +URL: https://github.com/atifaziz/NCrontab +License: Apache License, Version 2.0 +Copyright: 2001 The OpenSymphony Group +Copyright: 2008 Atif Aziz + +--- + +Nerdbank.GitVersioning: A library for versioning .NET projects + +URL: https://github.com/dotnet/Nerdbank.GitVersioning +License: MIT License +Copyright: .NET Foundation and Contributors + +--- + +NJsonSchema: A JSON schema validator for .NET + +URL: https://github.com/RicoSuter/NJsonSchema +License: MIT License +Copyright: 2022 Rico Suter + +--- + +NPoco: A micro ORM for .NET + +URL: https://github.com/schotime/NPoco +License: Apache License, Version 2.0 +Copyright: Schotime + +--- + +NUnit: A unit testing framework for .NET + +URL: https://github.com/nunit/nunit +License: MIT License +Copyright: Charlie Poole, Rob Prouse and Contributors + +--- + +Open Web Components: A set of standards and libraries for building web components + +URL: https://open-wc.org/ +License: MIT License +Copyright: 2018 open-wc + +--- + +Openapi-ts: The OpenAPI to TypeScript codegen + +URL: https://github.com/hey-api/openapi-ts +License: MIT License +Copyright: Hey API + +--- + +OpenIddict: A simple and flexible OpenID Connect server for ASP.NET Core + +URL: https://github.com/openiddict/openiddict-core +License: Apache License, Version 2.0 +Copyright: Kévin Chalet + +--- + +Playwright: A Node.js library to automate browser testing + +URL: https://playwright.dev/ +License: Apache License, Version 2.0 +Copyright: 2025 Microsoft Corporation + +--- + +Playwright-msw: A library to wrap Mock Service Worker with Playwright + +URL: https://github.com/valendres/playwright-msw +License: MIT License +Copyright: 2022 Peter Weller + +--- + +Prettier: An opinionated code formatter + +URL: https://prettier.io/ +License: MIT License +Copyright: James Long and contributors + +--- + +Remark-gfm: A GitHub Flavored Markdown plugin for Remark + +URL: https://github.com/remarkjs/remark-gfm +License: MIT License +Copyright: Titus Wormer + +--- + +Rollup: A module bundler for JavaScript + +URL: https://rollupjs.org/ +License: MIT License +Copyright: 2015-present Rollup contributors + +--- + +Rollup Plugins: A collection of Rollup plugins + +URL: https://github.com/rollup/plugins +License: MIT License +Copyright: 2019-present Rollup Plugins contributors + +--- + +Rollup-plugin-esbuild: A Rollup plugin for using esbuild + +URL: https://github.com/egoist/rollup-plugin-esbuild +License: MIT License +Copyright: 2020 EGOIST + +--- + +Rollup-plugin-import-css: A Rollup plugin for importing CSS files + +URL: https://github.com/jleeson/rollup-plugin-import-css +License: MIT License +Copyright: 2020 Jacob Leeson + +--- + +rxjs: Reactive Extensions for JavaScript + +URL: https://rxjs.dev/ +License: Apache License, Version 2.0 +Copyright: 2015-present Ben Lesh , Google, Inc., Netflix, Inc., Microsoft Corp., and contributors + +--- + +Serilog: A diagnostic logging library for .NET + +URL: https://github.com/serilog/serilog +License: Apache License, Version 2.0 +Copyright: Serilog Contributors + +--- + +Simple Icons: A set of SVG icons for popular brands + +URL: https://simpleicons.org/ +License: CC0 1.0 Universal License +Copyright: Simple Icons Contributors + +--- + +Storybook: A UI component explorer for Web Components + +URL: https://storybook.js.org/ +License: MIT License +Copyright: 2024 Storybook + +--- + +StyleCop.Analyzers: Analyzers for StyleCop + +URL: https://github.com/DotNetAnalyzers/StyleCopAnalyzers +License: MIT License +Copyright: Tunnel Vision Laboratories, LLC + +--- + +SVGO: A tool for optimizing SVG files + +URL: https://svgo.dev/ +License: MIT License +Copyright: Kir Belevich + +--- + +Swashbuckle.AspNetCore: A library for generating Swagger documentation for ASP.NET Core APIs + +URL: https://github.com/domaindrivendev/Swashbuckle.AspNetCore +License: MIT License +Copyright: 2016 Richard Morris + +--- + +Tiny Glob: A tiny globbing library for Node.js + +URL: https://github.com/terkelg/tiny-glob +License: MIT License +Copyright: 2018 Terkel + +--- + +Tiptap: A renderless rich-text editor for the web + +URL: https://tiptap.dev/ +License: MIT License +Copyright: 2025 Tiptap GmbH + +--- + +Tsc-alias: A TypeScript compiler plugin for aliasing module paths + +URL: https://github.com/justkey007/tsc-alias +License: MIT License +Copyright: 2018 Justkey + +--- + +Typedoc: A documentation generator for TypeScript projects + +URL: https://typedoc.org/ +License: Apache License, Version 2.0 +Copyright: Gerrit Birkeland and Contributors + +--- + +Typescript: A typed superset of JavaScript that compiles to plain JavaScript + +URL: https://www.typescriptlang.org/ +License: Apache License, Version 2.0 +Copyright: 2012-present Microsoft Corporation + +--- + +Typescript-eslint: A set of tools for linting TypeScript code + +URL: https://github.com/typescript-eslint/typescript-eslint +License: MIT License +Copyright: 2019 typescript-eslint and other contributors + +--- + +Typescript-json-schema: A library for generating JSON schema from TypeScript types + +URL: https://github.com/YousefED/typescript-json-schema +License: BSD 3-Clause License +Copyright: 2016 typescript-json-schema contributors + +--- + +Umbraco.Code: Provides code-level tools for Umbraco + +URL: https://github.com/umbraco/Umbraco-Code +License: MIT License +Copyright: 2005-present Umbraco A/S + +--- + +Umbraco.GitVersioning.Extensions: Utilities for Nerdbank.GitVersioning + +URL: https://github.com/umbraco/Umbraco.GitVersioning.Extensions +License: MIT License +Copyright: 2005-present Umbraco A/S + +--- + +Umbraco.JsonSchema.Extensions: Utilities for JSON schema generation + +URL: https://github.com/umbraco/Umbraco.JsonSchema.Extensions +License: MIT License +Copyright: 2005-present Umbraco A/S + +--- + +Umbraco UI Library: A set of UI components for building web applications + +URL: https://uui.umbraco.com/ +License: MIT License +Copyright: 2005-present Umbraco A/S + +--- + +uuid: A library for generating unique identifiers + +URL: https://github.com/uuidjs/uuid +License: MIT License +Copyright: 2010-2020 Robert Kieffer and other contributors + +--- + +Vite: A fast build tool and development server for modern web projects + +URL: https://vite.dev/ +License: MIT License +Copyright: 2019-present VoidZero Inc. and Vite contributors + +--- + +Vite-plugin-static-copy: A Vite plugin for copying static files + +URL: https://github.com/sapphi-red/vite-plugin-static-copy +License: MIT License +Copyright: 2021 sapphi-red + +--- + +Vite-tsconfig-paths: A Vite plugin for resolving TypeScript paths + +URL: https://github.com/aleclarson/vite-tsconfig-paths +License: MIT License +Copyright: Alec Larson + +--- + +Web Component Analyzer: A tool for analyzing web components + +URL: https://github.com/runem/web-component-analyzer +License: MIT License +Copyright: 2019 Rune Mehlsen diff --git a/Umbraco.Client.code-workspace b/Umbraco.Client.code-workspace new file mode 100644 index 0000000000..74e21add7e --- /dev/null +++ b/Umbraco.Client.code-workspace @@ -0,0 +1,10 @@ +{ + "folders": [ + { + "path": "src/Umbraco.Web.UI.Client" + }, + { + "path": "src/Umbraco.Web.UI.Login" + } + ] +} \ No newline at end of file diff --git a/build/nightly-E2E-test-pipelines.yml b/build/nightly-E2E-test-pipelines.yml index 8cbb065cd7..2cb8237e25 100644 --- a/build/nightly-E2E-test-pipelines.yml +++ b/build/nightly-E2E-test-pipelines.yml @@ -10,6 +10,7 @@ schedules: include: - v14/dev - v15/dev + - v16/dev variables: nodeVersion: 20 @@ -428,7 +429,7 @@ stages: inputs: targetPath: $(Build.ArtifactStagingDirectory) artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" - + # Publish test results - task: PublishTestResults@2 displayName: "Publish test results" diff --git a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs index d569d0a6a5..e4b60877f1 100644 --- a/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Common/Configuration/ConfigureUmbracoSwaggerGenOptions.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using Umbraco.Cms.Api.Common.OpenApi; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Api.Common.Configuration; @@ -18,13 +17,6 @@ public class ConfigureUmbracoSwaggerGenOptions : IConfigureOptions()) - { } - public ConfigureUmbracoSwaggerGenOptions( IOperationIdSelector operationIdSelector, ISchemaIdSelector schemaIdSelector, diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/IOperationIdSelector.cs b/src/Umbraco.Cms.Api.Common/OpenApi/IOperationIdSelector.cs index 3f97e78165..441279a66b 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/IOperationIdSelector.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/IOperationIdSelector.cs @@ -5,8 +5,5 @@ namespace Umbraco.Cms.Api.Common.OpenApi; public interface IOperationIdSelector { - [Obsolete("Use overload that only takes ApiDescription instead. This will be removed in Umbraco 15.")] - string? OperationId(ApiDescription apiDescription, ApiVersioningOptions apiVersioningOptions); - string? OperationId(ApiDescription apiDescription); } diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/OperationIdSelector.cs b/src/Umbraco.Cms.Api.Common/OpenApi/OperationIdSelector.cs index 3c00a126be..8a599b2721 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/OperationIdSelector.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/OperationIdSelector.cs @@ -16,9 +16,6 @@ public class OperationIdSelector : IOperationIdSelector public OperationIdSelector(IEnumerable operationIdHandlers) => _operationIdHandlers = operationIdHandlers; - [Obsolete("Use overload that only takes ApiDescription instead. This will be removed in Umbraco 15.")] - public virtual string? OperationId(ApiDescription apiDescription, ApiVersioningOptions apiVersioningOptions) => OperationId(apiDescription); - public virtual string? OperationId(ApiDescription apiDescription) { IOperationIdHandler? handler = _operationIdHandlers.FirstOrDefault(h => h.CanHandle(apiDescription)); diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/SubTypesSelector.cs b/src/Umbraco.Cms.Api.Common/OpenApi/SubTypesSelector.cs index c6e172f3f7..947e7a8197 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/SubTypesSelector.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/SubTypesSelector.cs @@ -10,12 +10,12 @@ namespace Umbraco.Cms.Api.Common.OpenApi; public class SubTypesSelector : ISubTypesSelector { - private readonly IOptions _settings; private readonly IHostingEnvironment _hostingEnvironment; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IEnumerable _subTypeHandlers; private readonly IUmbracoJsonTypeInfoResolver _umbracoJsonTypeInfoResolver; + [Obsolete("The settings parameter is not required anymore, use the other constructor instead. Scheduled for removal in Umbraco 17.")] public SubTypesSelector( IOptions settings, IHostingEnvironment hostingEnvironment, @@ -23,7 +23,18 @@ public class SubTypesSelector : ISubTypesSelector IEnumerable subTypeHandlers, IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver) { - _settings = settings; + _hostingEnvironment = hostingEnvironment; + _httpContextAccessor = httpContextAccessor; + _subTypeHandlers = subTypeHandlers; + _umbracoJsonTypeInfoResolver = umbracoJsonTypeInfoResolver; + } + + public SubTypesSelector( + IHostingEnvironment hostingEnvironment, + IHttpContextAccessor httpContextAccessor, + IEnumerable subTypeHandlers, + IUmbracoJsonTypeInfoResolver umbracoJsonTypeInfoResolver) + { _hostingEnvironment = hostingEnvironment; _httpContextAccessor = httpContextAccessor; _subTypeHandlers = subTypeHandlers; @@ -32,7 +43,7 @@ public class SubTypesSelector : ISubTypesSelector public IEnumerable SubTypes(Type type) { - var backOfficePath = _settings.Value.GetBackOfficePath(_hostingEnvironment); + var backOfficePath = _hostingEnvironment.GetBackOfficePath(); var swaggerPath = $"{backOfficePath}/swagger"; if (_httpContextAccessor.HttpContext?.Request.Path.StartsWithSegments(swaggerPath) ?? false) diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs b/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs index 9f05f57d5a..dba0e6f56d 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/SwaggerRouteTemplatePipelineFilter.cs @@ -7,7 +7,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerUI; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Web.Common.ApplicationBuilder; using Umbraco.Extensions; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; @@ -30,24 +30,21 @@ public class SwaggerRouteTemplatePipelineFilter : UmbracoPipelineFilter IOptions swaggerGenOptions = applicationBuilder.ApplicationServices.GetRequiredService>(); applicationBuilder.UseSwagger(swaggerOptions => - { - swaggerOptions.RouteTemplate = SwaggerRouteTemplate(applicationBuilder); - }); + { + swaggerOptions.RouteTemplate = SwaggerRouteTemplate(applicationBuilder); + }); applicationBuilder.UseSwaggerUI(swaggerUiOptions => SwaggerUiConfiguration(swaggerUiOptions, swaggerGenOptions.Value, applicationBuilder)); } protected virtual bool SwaggerIsEnabled(IApplicationBuilder applicationBuilder) - { - IWebHostEnvironment webHostEnvironment = applicationBuilder.ApplicationServices.GetRequiredService(); - return webHostEnvironment.IsProduction() is false; - } + => applicationBuilder.ApplicationServices.GetRequiredService().IsProduction() is false; protected virtual string SwaggerRouteTemplate(IApplicationBuilder applicationBuilder) - => $"{GetUmbracoPath(applicationBuilder).TrimStart(Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json"; + => $"{GetBackOfficePath(applicationBuilder).TrimStart(Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json"; protected virtual string SwaggerUiRoutePrefix(IApplicationBuilder applicationBuilder) - => $"{GetUmbracoPath(applicationBuilder).TrimStart(Constants.CharArrays.ForwardSlash)}/swagger"; + => $"{GetBackOfficePath(applicationBuilder).TrimStart(Constants.CharArrays.ForwardSlash)}/swagger"; protected virtual void SwaggerUiConfiguration( SwaggerUIOptions swaggerUiOptions, @@ -56,8 +53,7 @@ public class SwaggerRouteTemplatePipelineFilter : UmbracoPipelineFilter { swaggerUiOptions.RoutePrefix = SwaggerUiRoutePrefix(applicationBuilder); - foreach ((var name, OpenApiInfo? apiInfo) in swaggerGenOptions.SwaggerGeneratorOptions.SwaggerDocs - .OrderBy(x => x.Value.Title)) + foreach ((var name, OpenApiInfo? apiInfo) in swaggerGenOptions.SwaggerGeneratorOptions.SwaggerDocs.OrderBy(x => x.Value.Title)) { swaggerUiOptions.SwaggerEndpoint($"{name}/swagger.json", $"{apiInfo.Title}"); } @@ -70,11 +66,6 @@ public class SwaggerRouteTemplatePipelineFilter : UmbracoPipelineFilter swaggerUiOptions.OAuthUsePkce(); } - private string GetUmbracoPath(IApplicationBuilder applicationBuilder) - { - GlobalSettings settings = applicationBuilder.ApplicationServices.GetRequiredService>().Value; - IHostingEnvironment hostingEnvironment = applicationBuilder.ApplicationServices.GetRequiredService(); - - return settings.GetBackOfficePath(hostingEnvironment); - } + private string GetBackOfficePath(IApplicationBuilder applicationBuilder) + => applicationBuilder.ApplicationServices.GetRequiredService().GetBackOfficePath(); } diff --git a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs index 1c821f9681..5eaa75002c 100644 --- a/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Delivery/Configuration/ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions.cs @@ -48,7 +48,7 @@ public class ConfigureUmbracoMemberAuthenticationDeliveryApiSwaggerGenOptions : Id = AuthSchemeName, } }, - new string[] { } + [] } } }; diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs index 523c496956..775b81803d 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -8,7 +7,6 @@ using Umbraco.Cms.Core.Models.PublishedContent; namespace Umbraco.Cms.Api.Delivery.Controllers.Content; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class ByIdContentApiController : ContentApiItemControllerBase { @@ -21,16 +19,6 @@ public class ByIdContentApiController : ContentApiItemControllerBase : base(apiPublishedContentCache, apiContentResponseBuilder) => _requestMemberAccessService = requestMemberAccessService; - [HttpGet("item/{id:guid}")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IApiContentResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task ById(Guid id) - => await HandleRequest(id); - /// /// Gets a content item by id. /// diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs index 0771c35758..150a1828ef 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs @@ -8,7 +8,6 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Delivery.Controllers.Content; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class ByIdsContentApiController : ContentApiItemControllerBase { @@ -21,15 +20,6 @@ public class ByIdsContentApiController : ContentApiItemControllerBase : base(apiPublishedContentCache, apiContentResponseBuilder) => _requestMemberAccessService = requestMemberAccessService; - [HttpGet("item")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task Item([FromQuery(Name = "id")] HashSet ids) - => await HandleRequest(ids); - /// /// Gets content items by ids. /// @@ -58,6 +48,6 @@ public class ByIdsContentApiController : ContentApiItemControllerBase .WhereNotNull() .ToArray(); - return await Task.FromResult(Ok(apiContentItems)); + return Ok(apiContentItems); } } diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs index 54fe5785a9..7bc549fe33 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs @@ -1,17 +1,13 @@ using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core; using Umbraco.Cms.Core.DeliveryApi; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Api.Delivery.Controllers.Content; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class ByRouteContentApiController : ContentApiItemControllerBase { @@ -21,45 +17,6 @@ public class ByRouteContentApiController : ContentApiItemControllerBase private readonly IRequestMemberAccessService _requestMemberAccessService; private const string PreviewContentRequestPathPrefix = $"/{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}"; - [Obsolete($"Please use the constructor that accepts {nameof(IApiContentPathResolver)}. Will be removed in V15.")] - public ByRouteContentApiController( - IApiPublishedContentCache apiPublishedContentCache, - IApiContentResponseBuilder apiContentResponseBuilder, - IRequestRoutingService requestRoutingService, - IRequestRedirectService requestRedirectService, - IRequestPreviewService requestPreviewService, - IRequestMemberAccessService requestMemberAccessService) - : this( - apiPublishedContentCache, - apiContentResponseBuilder, - requestRedirectService, - requestPreviewService, - requestMemberAccessService, - StaticServiceProvider.Instance.GetRequiredService()) - { - } - - [Obsolete($"Please use the non-obsolete constructor. Will be removed in V15.")] - public ByRouteContentApiController( - IApiPublishedContentCache apiPublishedContentCache, - IApiContentResponseBuilder apiContentResponseBuilder, - IPublicAccessService publicAccessService, - IRequestRoutingService requestRoutingService, - IRequestRedirectService requestRedirectService, - IRequestPreviewService requestPreviewService, - IRequestMemberAccessService requestMemberAccessService, - IApiContentPathResolver apiContentPathResolver) - : this( - apiPublishedContentCache, - apiContentResponseBuilder, - requestRedirectService, - requestPreviewService, - requestMemberAccessService, - apiContentPathResolver) - { - } - - [ActivatorUtilitiesConstructor] public ByRouteContentApiController( IApiPublishedContentCache apiPublishedContentCache, IApiContentResponseBuilder apiContentResponseBuilder, @@ -75,16 +32,6 @@ public class ByRouteContentApiController : ContentApiItemControllerBase _apiContentPathResolver = apiContentPathResolver; } - [HttpGet("item/{*path}")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IApiContentResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task ByRoute(string path = "") - => await HandleRequest(path); - /// /// Gets a content item by route. /// @@ -117,7 +64,7 @@ public class ByRouteContentApiController : ContentApiItemControllerBase return deniedAccessResult; } - return await Task.FromResult(Ok(ApiContentResponseBuilder.Build(contentItem))); + return Ok(ApiContentResponseBuilder.Build(contentItem)); } IApiContentRoute? redirectRoute = _requestRedirectService.GetRedirectRoute(path); diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs index bfa45f23ec..c46329d914 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs @@ -12,7 +12,6 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Delivery.Controllers.Content; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class QueryContentApiController : ContentApiControllerBase { @@ -30,20 +29,6 @@ public class QueryContentApiController : ContentApiControllerBase _requestMemberAccessService = requestMemberAccessService; } - [HttpGet] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task Query( - string? fetch, - [FromQuery] string[] filter, - [FromQuery] string[] sort, - int skip = 0, - int take = 10) - => await HandleRequest(fetch, filter, sort, skip, take); - /// /// Gets a paginated list of content item(s) from query. /// diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs index f7fa2f6d92..0c82a652ef 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Models.DeliveryApi; @@ -8,7 +8,6 @@ using Umbraco.Cms.Infrastructure.DeliveryApi; namespace Umbraco.Cms.Api.Delivery.Controllers.Media; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class ByIdMediaApiController : MediaApiControllerBase { @@ -19,14 +18,6 @@ public class ByIdMediaApiController : MediaApiControllerBase { } - [HttpGet("item/{id:guid}")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IApiMediaWithCropsResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task ById(Guid id) - => await HandleRequest(id); - /// /// Gets a media item by id. /// @@ -36,16 +27,16 @@ public class ByIdMediaApiController : MediaApiControllerBase [MapToApiVersion("2.0")] [ProducesResponseType(typeof(IApiMediaWithCropsResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task ByIdV20(Guid id) - => await HandleRequest(id); + public Task ByIdV20(Guid id) + => Task.FromResult(HandleRequest(id)); - private async Task HandleRequest(Guid id) + private IActionResult HandleRequest(Guid id) { IPublishedContent? media = PublishedMediaCache.GetById(id); if (media is null) { - return await Task.FromResult(NotFound()); + return NotFound(); } return Ok(BuildApiMediaWithCrops(media)); diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs index 957c982c38..45fad21cf9 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs @@ -9,7 +9,6 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Delivery.Controllers.Media; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class ByIdsMediaApiController : MediaApiControllerBase { @@ -18,13 +17,6 @@ public class ByIdsMediaApiController : MediaApiControllerBase { } - [HttpGet("item")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task Item([FromQuery(Name = "id")] HashSet ids) - => await HandleRequest(ids); - /// /// Gets media items by ids. /// @@ -33,10 +25,10 @@ public class ByIdsMediaApiController : MediaApiControllerBase [HttpGet("items")] [MapToApiVersion("2.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task ItemsV20([FromQuery(Name = "id")] HashSet ids) - => await HandleRequest(ids); + public Task ItemsV20([FromQuery(Name = "id")] HashSet ids) + => Task.FromResult(HandleRequest(ids)); - private async Task HandleRequest(HashSet ids) + private IActionResult HandleRequest(HashSet ids) { IPublishedContent[] mediaItems = ids .Select(PublishedMediaCache.GetById) @@ -47,6 +39,6 @@ public class ByIdsMediaApiController : MediaApiControllerBase .Select(BuildApiMediaWithCrops) .ToArray(); - return await Task.FromResult(Ok(apiMediaItems)); + return Ok(apiMediaItems); } } diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs index 0afedddffb..c2b91d0fd1 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.DeliveryApi; @@ -9,7 +9,6 @@ using Umbraco.Cms.Infrastructure.DeliveryApi; namespace Umbraco.Cms.Api.Delivery.Controllers.Media; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class ByPathMediaApiController : MediaApiControllerBase { @@ -22,14 +21,6 @@ public class ByPathMediaApiController : MediaApiControllerBase : base(publishedMediaCache, apiMediaWithCropsResponseBuilder) => _apiMediaQueryService = apiMediaQueryService; - [HttpGet("item/{*path}")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(IApiMediaWithCropsResponse), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task ByPath(string path) - => await HandleRequest(path); - /// /// Gets a media item by its path. /// @@ -39,17 +30,17 @@ public class ByPathMediaApiController : MediaApiControllerBase [MapToApiVersion("2.0")] [ProducesResponseType(typeof(IApiMediaWithCropsResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task ByPathV20(string path) - => await HandleRequest(path); + public Task ByPathV20(string path) + => Task.FromResult(HandleRequest(path)); - private async Task HandleRequest(string path) + private IActionResult HandleRequest(string path) { path = DecodePath(path); IPublishedContent? media = _apiMediaQueryService.GetByPath(path); if (media is null) { - return await Task.FromResult(NotFound()); + return NotFound(); } return Ok(BuildApiMediaWithCrops(media)); diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs index c61aaf8764..7c7238c850 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; @@ -14,7 +14,6 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Delivery.Controllers.Media; -[ApiVersion("1.0")] [ApiVersion("2.0")] public class QueryMediaApiController : MediaApiControllerBase { @@ -27,19 +26,6 @@ public class QueryMediaApiController : MediaApiControllerBase : base(publishedMediaCache, apiMediaWithCropsResponseBuilder) => _apiMediaQueryService = apiMediaQueryService; - [HttpGet] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] - [Obsolete("Please use version 2 of this API. Will be removed in V15.")] - public async Task Query( - string? fetch, - [FromQuery] string[] filter, - [FromQuery] string[] sort, - int skip = 0, - int take = 10) - => await HandleRequest(fetch, filter, sort, skip, take); - /// /// Gets a paginated list of media item(s) from query. /// @@ -53,15 +39,15 @@ public class QueryMediaApiController : MediaApiControllerBase [MapToApiVersion("2.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] - public async Task QueryV20( + public Task QueryV20( string? fetch, [FromQuery] string[] filter, [FromQuery] string[] sort, int skip = 0, int take = 10) - => await HandleRequest(fetch, filter, sort, skip, take); + => Task.FromResult(HandleRequest(fetch, filter, sort, skip, take)); - private async Task HandleRequest(string? fetch, string[] filter, string[] sort, int skip, int take) + private IActionResult HandleRequest(string? fetch, string[] filter, string[] sort, int skip, int take) { Attempt, ApiMediaQueryOperationStatus> queryAttempt = _apiMediaQueryService.ExecuteQuery(fetch, filter, sort, skip, take); @@ -79,6 +65,6 @@ public class QueryMediaApiController : MediaApiControllerBase Items = mediaItems.Select(BuildApiMediaWithCrops) }; - return await Task.FromResult(Ok(model)); + return Ok(model); } } diff --git a/src/Umbraco.Cms.Api.Delivery/Controllers/Security/MemberController.cs b/src/Umbraco.Cms.Api.Delivery/Controllers/Security/MemberController.cs index a5b085073b..139b9b2263 100644 --- a/src/Umbraco.Cms.Api.Delivery/Controllers/Security/MemberController.cs +++ b/src/Umbraco.Cms.Api.Delivery/Controllers/Security/MemberController.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OpenIddict.Abstractions; @@ -13,7 +12,6 @@ using OpenIddict.Server.AspNetCore; using Umbraco.Cms.Api.Delivery.Routing; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; @@ -33,31 +31,6 @@ public class MemberController : DeliveryApiControllerBase private readonly DeliveryApiSettings _deliveryApiSettings; private readonly ILogger _logger; - - [Obsolete("Please use the non-obsolete constructor. Will be removed in V16.")] - public MemberController( - IHttpContextAccessor httpContextAccessor, - IMemberSignInManager memberSignInManager, - IMemberManager memberManager, - IOptions deliveryApiSettings, - ILogger logger) - : this(memberSignInManager, memberManager, StaticServiceProvider.Instance.GetRequiredService(), deliveryApiSettings, logger) - { - } - - [Obsolete("Please use the non-obsolete constructor. Will be removed in V16.")] - public MemberController( - IHttpContextAccessor httpContextAccessor, - IMemberSignInManager memberSignInManager, - IMemberManager memberManager, - IMemberClientCredentialsManager memberClientCredentialsManager, - IOptions deliveryApiSettings, - ILogger logger) - : this(memberSignInManager, memberManager, memberClientCredentialsManager, deliveryApiSettings, logger) - { - } - - [ActivatorUtilitiesConstructor] public MemberController( IMemberSignInManager memberSignInManager, IMemberManager memberManager, diff --git a/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs index 89065a5d2e..cca2c8e09b 100644 --- a/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs @@ -62,7 +62,6 @@ public static class UmbracoBuilderExtensions builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); - builder.Services.AddScoped(); builder.Services.ConfigureOptions(); builder.AddUmbracoApiOpenApiUI(); diff --git a/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs b/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs index 43653239b3..fdd98e1702 100644 --- a/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs +++ b/src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs @@ -91,7 +91,7 @@ internal sealed class InitializeMemberApplicationNotificationHandler : INotifica } } - private bool ValidateRedirectUrls(Uri[] redirectUrls) + private bool ValidateRedirectUrls(IEnumerable redirectUrls) { if (redirectUrls.Any() is false) { diff --git a/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs b/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs index 0eeeb4d6da..15ecaa1423 100644 --- a/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs +++ b/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs @@ -185,7 +185,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService } - private Attempt, ApiMediaQueryOperationStatus> PagedResult(IEnumerable children, int skip, int take) + private static Attempt, ApiMediaQueryOperationStatus> PagedResult(IEnumerable children, int skip, int take) { IPublishedContent[] childrenAsArray = children as IPublishedContent[] ?? children.ToArray(); var result = new PagedModel diff --git a/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs b/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs index c6ff576b93..0416e5c303 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/BackOfficeLoginController.cs @@ -24,7 +24,7 @@ public class BackOfficeLoginModel public bool UserIsAlreadyLoggedIn { get; set; } } -[ApiExplorerSettings(IgnoreApi=true)] +[ApiExplorerSettings(IgnoreApi = true)] [Route(LoginPath)] public class BackOfficeLoginController : Controller { @@ -51,7 +51,7 @@ public class BackOfficeLoginController : Controller if (string.IsNullOrEmpty(model.UmbracoUrl)) { - model.UmbracoUrl = _hostingEnvironment.ToAbsolute(Constants.System.DefaultUmbracoPath); + model.UmbracoUrl = _hostingEnvironment.GetBackOfficePath(); } if (string.IsNullOrEmpty(model.ReturnUrl)) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs index 20d6770444..eb5177996a 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Culture/AllCultureController.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -28,7 +28,7 @@ public class AllCultureController : CultureControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> GetAll(CancellationToken cancellationToken, int skip = 0, int take = 100) + public Task> GetAll(CancellationToken cancellationToken, int skip = 0, int take = 100) { CultureInfo[] all = _cultureService.GetValidCultureInfos(); @@ -38,6 +38,6 @@ public class AllCultureController : CultureControllerBase Total = all.Length }; - return await Task.FromResult(viewModel); + return Task.FromResult(viewModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DataType/References/ReferencedByDataTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DataType/References/ReferencedByDataTypeController.cs new file mode 100644 index 0000000000..b36815d408 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/DataType/References/ReferencedByDataTypeController.cs @@ -0,0 +1,46 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.DataType.References; + +[ApiVersion("1.0")] +public class ReferencedByDataTypeController : DataTypeControllerBase +{ + private readonly IDataTypeService _dataTypeService; + private readonly IRelationTypePresentationFactory _relationTypePresentationFactory; + + public ReferencedByDataTypeController(IDataTypeService dataTypeService, IRelationTypePresentationFactory relationTypePresentationFactory) + { + _dataTypeService = dataTypeService; + _relationTypePresentationFactory = relationTypePresentationFactory; + } + + /// + /// Gets a paged list of references for the current data type, so you can see where it is being used. + /// + [HttpGet("{id:guid}/referenced-by")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> ReferencedBy( + CancellationToken cancellationToken, + Guid id, + int skip = 0, + int take = 20) + { + PagedModel relationItems = await _dataTypeService.GetPagedRelationsAsync(id, skip, take); + + var pagedViewModel = new PagedViewModel + { + Total = relationItems.Total, + Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items), + }; + + return pagedViewModel; + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DataType/ReferencesDataTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DataType/ReferencesDataTypeController.cs index c25586e93c..0eee28e49b 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DataType/ReferencesDataTypeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DataType/ReferencesDataTypeController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Api.Management.Controllers.DataType; [ApiVersion("1.0")] +[Obsolete("Please use ReferencedByDataTypeController and the referenced-by endpoint. Scheduled for removal in Umbraco 17.")] public class ReferencesDataTypeController : DataTypeControllerBase { private readonly IDataTypeService _dataTypeService; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs index b40db7607a..a0e7747243 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/AllDictionaryController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Mapping; @@ -38,6 +38,6 @@ public class AllDictionaryController : DictionaryControllerBase Total = items.Length, Items = _umbracoMapper.MapEnumerable(items.Skip(skip).Take(take)) }; - return await Task.FromResult(model); + return model; } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs index 5e55924079..2b02ff541a 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs @@ -28,7 +28,7 @@ public class DictionaryTreeControllerBase : NamedEntityTreeControllerBase> MapTreeItemViewModels(IEnumerable dictionaryItems) - => await Task.WhenAll(dictionaryItems.Select(CreateEntityTreeItemViewModelAsync)); + => await MapTreeItemViewModelsAsync(dictionaryItems).ToArrayAsync(); protected override async Task>> GetAncestors(Guid descendantKey, bool includeSelf = true) { @@ -54,10 +54,19 @@ public class DictionaryTreeControllerBase : NamedEntityTreeControllerBase MapTreeItemViewModelsAsync(IEnumerable dictionaryItems) + { + foreach (IDictionaryItem dictionaryItem in dictionaryItems) + { + yield return await CreateEntityTreeItemViewModelAsync(dictionaryItem); + } + } + private async Task CreateEntityTreeItemViewModelAsync(IDictionaryItem dictionaryItem) { var hasChildren = await DictionaryItemService.CountChildrenAsync(dictionaryItem.Key) > 0; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/Collection/ByKeyDocumentCollectionController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/Collection/ByKeyDocumentCollectionController.cs index 6f2dd43824..f55c666cfc 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/Collection/ByKeyDocumentCollectionController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/Collection/ByKeyDocumentCollectionController.cs @@ -1,12 +1,10 @@ using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.Document.Collection; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; @@ -22,20 +20,6 @@ public class ByKeyDocumentCollectionController : DocumentCollectionControllerBas private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; private readonly IDocumentCollectionPresentationFactory _documentCollectionPresentationFactory; - [Obsolete("Please use the constructor taking all parameters.")] - public ByKeyDocumentCollectionController( - IContentListViewService contentListViewService, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IUmbracoMapper mapper) - : this( - contentListViewService, - backOfficeSecurityAccessor, - mapper, - StaticServiceProvider.Instance.GetRequiredService()) - { - } - - [ActivatorUtilitiesConstructor] public ByKeyDocumentCollectionController( IContentListViewService contentListViewService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/DocumentControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/DocumentControllerBase.cs index 52b2f34d7f..10f1931313 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/DocumentControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/DocumentControllerBase.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Controllers.Content; @@ -140,6 +140,10 @@ public abstract class DocumentControllerBase : ContentControllerBase .WithDetail( "An unspecified error occurred while (un)publishing. Please check the logs for additional information.") .Build()), + ContentPublishingOperationStatus.TaskResultNotFound => NotFound(problemDetailsBuilder + .WithTitle("The result of the submitted task could not be found") + .Build()), + _ => StatusCode(StatusCodes.Status500InternalServerError, "Unknown content operation status."), }); diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/ItemDocumentItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/ItemDocumentItemController.cs index bea90daf42..ebefb5bfb2 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/ItemDocumentItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/ItemDocumentItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -24,13 +24,13 @@ public class ItemDocumentItemController : DocumentItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable documents = _entityService @@ -38,6 +38,6 @@ public class ItemDocumentItemController : DocumentItemControllerBase .OfType(); IEnumerable documentItemResponseModels = documents.Select(_documentPresentationFactory.CreateItemResponseModel); - return await Task.FromResult(Ok(documentItemResponseModels)); + return Task.FromResult(Ok(documentItemResponseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs index 1e8bb5054b..62e15d7e74 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs @@ -1,4 +1,3 @@ -using System.Text.Json.Serialization; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -22,28 +21,6 @@ public class SearchDocumentItemController : DocumentItemControllerBase _documentPresentationFactory = documentPresentationFactory; } - [NonAction] - [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - public async Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) - => await SearchFromParent(cancellationToken, query, skip, take); - - [NonAction] - [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - public async Task SearchFromParent(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, Guid? parentId = null) - => await SearchWithTrashed(cancellationToken, query, null, skip, take, parentId); - - [NonAction] - [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] - public async Task SearchFromParentWithAllowedTypes( - CancellationToken cancellationToken, - string query, - int skip = 0, - int take = 100, - Guid? parentId = null, - [FromQuery] IEnumerable? allowedDocumentTypes = null) => - await SearchWithTrashed(cancellationToken, query, null, skip, take, parentId, allowedDocumentTypes); - [HttpGet("search")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] @@ -56,13 +33,13 @@ public class SearchDocumentItemController : DocumentItemControllerBase Guid? parentId = null, [FromQuery] IEnumerable? allowedDocumentTypes = null) { - PagedModel searchResult = _indexedEntitySearchService.Search(UmbracoObjectTypes.Document, query, parentId, allowedDocumentTypes, trashed, skip, take); + PagedModel searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Document, query, parentId, allowedDocumentTypes, trashed, skip, take); var result = new PagedModel { Items = searchResult.Items.OfType().Select(_documentPresentationFactory.CreateItemResponseModel), Total = searchResult.Total, }; - return await Task.FromResult(Ok(result)); + return Ok(result); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsController.cs index 869bc4c880..f9d9681f07 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsController.cs @@ -35,7 +35,7 @@ public class PublishDocumentWithDescendantsController : DocumentControllerBase [HttpPut("{id:guid}/publish-with-descendants")] [MapToApiVersion("1.0")] - [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(typeof(PublishWithDescendantsResultModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task PublishWithDescendants(CancellationToken cancellationToken, Guid id, PublishDocumentWithDescendantsRequestModel requestModel) @@ -54,10 +54,15 @@ public class PublishDocumentWithDescendantsController : DocumentControllerBase id, requestModel.Cultures, BuildPublishBranchFilter(requestModel), - CurrentUserKey(_backOfficeSecurityAccessor)); + CurrentUserKey(_backOfficeSecurityAccessor), + true); - return attempt.Success - ? Ok() + return attempt.Success && attempt.Result.AcceptedTaskId.HasValue + ? Ok(new PublishWithDescendantsResultModel + { + TaskId = attempt.Result.AcceptedTaskId.Value, + IsComplete = false + }) : DocumentPublishingOperationStatusResult(attempt.Status, failedBranchItems: attempt.Result.FailedItems); } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsResultController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsResultController.cs new file mode 100644 index 0000000000..9a499ede1e --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/PublishDocumentWithDescendantsResultController.cs @@ -0,0 +1,69 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.ViewModels.Document; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Actions; +using Umbraco.Cms.Core.Models.ContentPublishing; +using Umbraco.Cms.Core.Security.Authorization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Management.Controllers.Document; + +[ApiVersion("1.0")] +public class PublishDocumentWithDescendantsResultController : DocumentControllerBase +{ + private readonly IAuthorizationService _authorizationService; + private readonly IContentPublishingService _contentPublishingService; + + public PublishDocumentWithDescendantsResultController( + IAuthorizationService authorizationService, + IContentPublishingService contentPublishingService) + { + _authorizationService = authorizationService; + _contentPublishingService = contentPublishingService; + } + + [HttpGet("{id:guid}/publish-with-descendants/result/{taskId:guid}")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PublishWithDescendantsResultModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task PublishWithDescendantsResult(CancellationToken cancellationToken, Guid id, Guid taskId) + { + AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync( + User, + ContentPermissionResource.Branch(ActionPublish.ActionLetter, id), + AuthorizationPolicies.ContentPermissionByResource); + + if (!authorizationResult.Succeeded) + { + return Forbidden(); + } + + // Check if the publishing task has completed, if not, return the status. + var isPublishing = await _contentPublishingService.IsPublishingBranchAsync(taskId); + if (isPublishing) + { + return Ok(new PublishWithDescendantsResultModel + { + TaskId = taskId, + IsComplete = false + }); + }; + + // If completed, get the result and return the status. + Attempt attempt = await _contentPublishingService.GetPublishBranchResultAsync(taskId); + return attempt.Success + ? Ok(new PublishWithDescendantsResultModel + { + TaskId = taskId, + IsComplete = true + }) + : DocumentPublishingOperationStatusResult(attempt.Status, failedBranchItems: attempt.Result.FailedItems); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/RecycleBin/ReferencedByDocumentRecycleBinController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/RecycleBin/ReferencedByDocumentRecycleBinController.cs new file mode 100644 index 0000000000..2b94fb443f --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/RecycleBin/ReferencedByDocumentRecycleBinController.cs @@ -0,0 +1,50 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.Document.RecycleBin; + +[ApiVersion("1.0")] +public class ReferencedByDocumentRecycleBinController : DocumentRecycleBinControllerBase +{ + private readonly ITrackedReferencesService _trackedReferencesService; + private readonly IRelationTypePresentationFactory _relationTypePresentationFactory; + + public ReferencedByDocumentRecycleBinController( + IEntityService entityService, + IDocumentPresentationFactory documentPresentationFactory, + ITrackedReferencesService trackedReferencesService, + IRelationTypePresentationFactory relationTypePresentationFactory) + : base(entityService, documentPresentationFactory) + { + _trackedReferencesService = trackedReferencesService; + _relationTypePresentationFactory = relationTypePresentationFactory; + } + + /// + /// Gets a paged list of tracked references for all items in the document recycle bin, so you can see where an item is being used. + /// + [HttpGet("referenced-by")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> ReferencedBy( + CancellationToken cancellationToken, + int skip = 0, + int take = 20) + { + PagedModel relationItems = await _trackedReferencesService.GetPagedRelationsForRecycleBinAsync(UmbracoObjectTypes.Document, skip, take, true); + + var pagedViewModel = new PagedViewModel + { + Total = relationItems.Total, + Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items), + }; + + return pagedViewModel; + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/Tree/DocumentTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/Tree/DocumentTreeControllerBase.cs index 33e451bdc5..cacf862b57 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/Tree/DocumentTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/Tree/DocumentTreeControllerBase.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Api.Management.Controllers.Tree; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.Routing; using Umbraco.Cms.Api.Management.Services.Entities; +using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.Tree; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; @@ -33,7 +34,7 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa AppCaches appCaches, IBackOfficeSecurityAccessor backofficeSecurityAccessor, IDocumentPresentationFactory documentPresentationFactory) - : base(entityService, userStartNodeEntitiesService, dataTypeService) + : base(entityService, userStartNodeEntitiesService, dataTypeService) { _publicAccessService = publicAccessService; _appCaches = appCaches; @@ -52,6 +53,8 @@ public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBa if (entity is IDocumentEntitySlim documentEntitySlim) { responseModel.IsProtected = _publicAccessService.IsProtected(entity.Path); + responseModel.Ancestors = EntityService.GetPathKeys(entity, omitSelf: true) + .Select(x => new ReferenceByIdModel(x)); responseModel.IsTrashed = entity.Trashed; responseModel.Id = entity.Key; responseModel.CreateDate = entity.CreateDate; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/UpdateNotificationsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/UpdateNotificationsController.cs index d02e1456f9..0d504b22a6 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/UpdateNotificationsController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/UpdateNotificationsController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.Document; @@ -35,6 +35,6 @@ public class UpdateNotificationsController : DocumentControllerBase } _notificationService.SetNotifications(_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, content, updateModel.SubscribedActionIds); - return await Task.FromResult(Ok()); + return Ok(); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateCreateDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateCreateDocumentController.cs index ecca82286c..7087e119f7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateCreateDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateCreateDocumentController.cs @@ -1,11 +1,14 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.OperationStatus; @@ -16,15 +19,32 @@ public class ValidateCreateDocumentController : CreateDocumentControllerBase { private readonly IDocumentEditingPresentationFactory _documentEditingPresentationFactory; private readonly IContentEditingService _contentEditingService; + private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")] public ValidateCreateDocumentController( IAuthorizationService authorizationService, IDocumentEditingPresentationFactory documentEditingPresentationFactory, IContentEditingService contentEditingService) + : this( + authorizationService, + documentEditingPresentationFactory, + contentEditingService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [ActivatorUtilitiesConstructor] + public ValidateCreateDocumentController( + IAuthorizationService authorizationService, + IDocumentEditingPresentationFactory documentEditingPresentationFactory, + IContentEditingService contentEditingService, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor) : base(authorizationService) { _documentEditingPresentationFactory = documentEditingPresentationFactory; _contentEditingService = contentEditingService; + _backOfficeSecurityAccessor = backOfficeSecurityAccessor; } [HttpPost("validate")] @@ -36,7 +56,10 @@ public class ValidateCreateDocumentController : CreateDocumentControllerBase => await HandleRequest(requestModel, async () => { ContentCreateModel model = _documentEditingPresentationFactory.MapCreateModel(requestModel); - Attempt result = await _contentEditingService.ValidateCreateAsync(model); + Attempt result = + await _contentEditingService.ValidateCreateAsync( + model, + CurrentUserKey(_backOfficeSecurityAccessor)); return result.Success ? Ok() diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateUpdateDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateUpdateDocumentController.cs index 05f029f582..bf06571c94 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateUpdateDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/ValidateUpdateDocumentController.cs @@ -1,58 +1,52 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.OperationStatus; namespace Umbraco.Cms.Api.Management.Controllers.Document; -[ApiVersion("1.0")] [ApiVersion("1.1")] public class ValidateUpdateDocumentController : UpdateDocumentControllerBase { private readonly IContentEditingService _contentEditingService; private readonly IDocumentEditingPresentationFactory _documentEditingPresentationFactory; + private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")] public ValidateUpdateDocumentController( IAuthorizationService authorizationService, IContentEditingService contentEditingService, IDocumentEditingPresentationFactory documentEditingPresentationFactory) + : this( + authorizationService, + contentEditingService, + documentEditingPresentationFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [ActivatorUtilitiesConstructor] + public ValidateUpdateDocumentController( + IAuthorizationService authorizationService, + IContentEditingService contentEditingService, + IDocumentEditingPresentationFactory documentEditingPresentationFactory, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor) : base(authorizationService) { _contentEditingService = contentEditingService; _documentEditingPresentationFactory = documentEditingPresentationFactory; + _backOfficeSecurityAccessor = backOfficeSecurityAccessor; } - [HttpPut("{id:guid}/validate")] - [MapToApiVersion("1.0")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - [Obsolete("Please use version 1.1 of this API. Will be removed in V16.")] - public async Task Validate(CancellationToken cancellationToken, Guid id, UpdateDocumentRequestModel requestModel) - => await HandleRequest(id, requestModel, async () => - { - var validateUpdateDocumentRequestModel = new ValidateUpdateDocumentRequestModel - { - Values = requestModel.Values, - Variants = requestModel.Variants, - Template = requestModel.Template, - Cultures = null - }; - - ValidateContentUpdateModel model = _documentEditingPresentationFactory.MapValidateUpdateModel(validateUpdateDocumentRequestModel); - Attempt result = await _contentEditingService.ValidateUpdateAsync(id, model); - - return result.Success - ? Ok() - : DocumentEditingOperationStatusResult(result.Status, requestModel, result.Result); - }); - [HttpPut("{id:guid}/validate")] [MapToApiVersion("1.1")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -62,7 +56,11 @@ public class ValidateUpdateDocumentController : UpdateDocumentControllerBase => await HandleRequest(id, requestModel, async () => { ValidateContentUpdateModel model = _documentEditingPresentationFactory.MapValidateUpdateModel(requestModel); - Attempt result = await _contentEditingService.ValidateUpdateAsync(id, model); + Attempt result = + await _contentEditingService.ValidateUpdateAsync( + id, + model, + CurrentUserKey(_backOfficeSecurityAccessor)); return result.Success ? Ok() diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DocumentBlueprint/Item/ItemDocumentBlueprintController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DocumentBlueprint/Item/ItemDocumentBlueprintController.cs index 5694d5b0b9..551f6cc407 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DocumentBlueprint/Item/ItemDocumentBlueprintController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DocumentBlueprint/Item/ItemDocumentBlueprintController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -25,13 +25,13 @@ public class ItemDocumentBlueprintController : DocumentBlueprintItemControllerBa [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable documents = _entityService @@ -39,6 +39,6 @@ public class ItemDocumentBlueprintController : DocumentBlueprintItemControllerBa .Select(x => x as IDocumentEntitySlim) .WhereNotNull(); IEnumerable responseModels = documents.Select(x => _documentPresentationFactory.CreateBlueprintItemResponseModel(x)); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/AllowedChildrenDocumentTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/AllowedChildrenDocumentTypeController.cs index f7403cb463..d44bd7ffd0 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/AllowedChildrenDocumentTypeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/AllowedChildrenDocumentTypeController.cs @@ -23,15 +23,6 @@ public class AllowedChildrenDocumentTypeController : DocumentTypeControllerBase _umbracoMapper = umbracoMapper; } - [NonAction] - [Obsolete("Use the non obsoleted method instead. Scheduled to be removed in v16")] - public async Task AllowedChildrenByKey( - CancellationToken cancellationToken, - Guid id, - int skip = 0, - int take = 100) - => await AllowedChildrenByKey(cancellationToken, id, null, skip, take); - [HttpGet("{id:guid}/allowed-children")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ConfigurationDocumentTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ConfigurationDocumentTypeController.cs index c1a4e41a75..74c5c58946 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ConfigurationDocumentTypeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ConfigurationDocumentTypeController.cs @@ -1,14 +1,9 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.DocumentType; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Features; using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.DocumentType; @@ -19,30 +14,8 @@ public class ConfigurationDocumentTypeController : DocumentTypeControllerBase { private readonly IConfigurationPresentationFactory _configurationPresentationFactory; - [ActivatorUtilitiesConstructor] public ConfigurationDocumentTypeController(IConfigurationPresentationFactory configurationPresentationFactory) - { - _configurationPresentationFactory = configurationPresentationFactory; - } - - [Obsolete("Use the constructor that only accepts IConfigurationPresentationFactory, scheduled for removal in V16")] - public ConfigurationDocumentTypeController( - UmbracoFeatures umbracoFeatures, - IOptionsSnapshot dataTypesSettings, - IOptionsSnapshot segmentSettings, - IConfigurationPresentationFactory configurationPresentationFactory) - : this(configurationPresentationFactory) - { - } - - [Obsolete("Use the constructor that only accepts IConfigurationPresentationFactory, scheduled for removal in V16")] - public ConfigurationDocumentTypeController( - UmbracoFeatures umbracoFeatures, - IOptionsSnapshot dataTypesSettings, - IOptionsSnapshot segmentSettings) - : this(StaticServiceProvider.Instance.GetRequiredService()) - { - } + => _configurationPresentationFactory = configurationPresentationFactory; [HttpGet("configuration")] [MapToApiVersion("1.0")] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/ItemDocumentTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/ItemDocumentTypeItemController.cs index 55211c7555..a4ed4eaa29 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/ItemDocumentTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/ItemDocumentTypeItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.DocumentType.Item; @@ -23,17 +23,17 @@ public class ItemDocumentTypeItemController : DocumentTypeItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable contentTypes = _contentTypeService.GetMany(ids); List responseModels = _mapper.MapEnumerable(contentTypes); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/SearchDocumentTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/SearchDocumentTypeItemController.cs index 0582c8e3e8..fd9a9217b0 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/SearchDocumentTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DocumentType/Item/SearchDocumentTypeItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/RollbackDocumentVersionController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/RollbackDocumentVersionController.cs index 1c024f3ef9..64f2f09bb3 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/RollbackDocumentVersionController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DocumentVersion/RollbackDocumentVersionController.cs @@ -2,10 +2,8 @@ using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Actions; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Security.Authorization; @@ -23,7 +21,6 @@ public class RollbackDocumentVersionController : DocumentVersionControllerBase private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; private readonly IAuthorizationService _authorizationService; - [ActivatorUtilitiesConstructor] public RollbackDocumentVersionController( IContentVersionService contentVersionService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, @@ -34,18 +31,6 @@ public class RollbackDocumentVersionController : DocumentVersionControllerBase _authorizationService = authorizationService; } - // TODO (V16): Remove this constructor. - [Obsolete("Please use the constructor taking all parameters. This constructor will be removed in V16.")] - public RollbackDocumentVersionController( - IContentVersionService contentVersionService, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor) - : this( - contentVersionService, - backOfficeSecurityAccessor, - StaticServiceProvider.Instance.GetRequiredService()) - { - } - [MapToApiVersion("1.0")] [HttpPost("{id:guid}/rollback")] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/DynamicRoot/GetQueryStepsController.cs b/src/Umbraco.Cms.Api.Management/Controllers/DynamicRoot/GetQueryStepsController.cs index 1271c2ab3f..5dc68f4522 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/DynamicRoot/GetQueryStepsController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/DynamicRoot/GetQueryStepsController.cs @@ -21,10 +21,10 @@ public class GetQueryStepsController : DynamicRootControllerBase [HttpGet($"steps")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task GetQuerySteps(CancellationToken cancellationToken) + public Task GetQuerySteps(CancellationToken cancellationToken) { IEnumerable querySteps = _dynamicRootQueryStepCollection.Select(x => x.SupportedDirectionAlias); - return await Task.FromResult(Ok(querySteps)); + return Task.FromResult(Ok(querySteps)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/ExecuteActionHealthCheckController.cs b/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/ExecuteActionHealthCheckController.cs index 6f3e5ea933..4a8583f4fd 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/ExecuteActionHealthCheckController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/ExecuteActionHealthCheckController.cs @@ -63,6 +63,6 @@ public class ExecuteActionHealthCheckController : HealthCheckControllerBase HealthCheckStatus result = await healthCheck.ExecuteActionAsync(_umbracoMapper.Map(action)!); - return await Task.FromResult(Ok(_umbracoMapper.Map(result))); + return Ok(_umbracoMapper.Map(result)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/AllHealthCheckGroupController.cs b/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/AllHealthCheckGroupController.cs index baaaf467bf..f41f352cc7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/AllHealthCheckGroupController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/AllHealthCheckGroupController.cs @@ -31,7 +31,7 @@ public class AllHealthCheckGroupController : HealthCheckGroupControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> All( + public Task>> All( CancellationToken cancellationToken, int skip = 0, int take = 100) @@ -46,6 +46,6 @@ public class AllHealthCheckGroupController : HealthCheckGroupControllerBase Items = _umbracoMapper.MapEnumerable, HealthCheckGroupResponseModel>(groups.Skip(skip).Take(take)) }; - return await Task.FromResult(Ok(viewModel)); + return Task.FromResult>>(Ok(viewModel)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/ByNameHealthCheckGroupController.cs b/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/ByNameHealthCheckGroupController.cs index 1aa97c6249..6ffa173e9b 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/ByNameHealthCheckGroupController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/HealthCheck/Group/ByNameHealthCheckGroupController.cs @@ -31,7 +31,7 @@ public class ByNameHealthCheckGroupController : HealthCheckGroupControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(HealthCheckGroupPresentationModel), StatusCodes.Status200OK)] - public async Task ByName( + public Task ByName( CancellationToken cancellationToken, string name) { @@ -42,9 +42,9 @@ public class ByNameHealthCheckGroupController : HealthCheckGroupControllerBase if (group is null) { - return HealthCheckGroupNotFound(); + return Task.FromResult(HealthCheckGroupNotFound()); } - return await Task.FromResult(Ok(_umbracoMapper.Map(group))); + return Task.FromResult(Ok(_umbracoMapper.Map(group))); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Help/GetHelpController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Help/GetHelpController.cs index 1c0ea568a1..e2227409b2 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Help/GetHelpController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Help/GetHelpController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -82,6 +82,5 @@ public class GetHelpController : HelpControllerBase return Ok(PagedViewModel.Empty()); } - private bool IsAllowedUrl(string? url) => - _helpPageSettings.HelpPageUrlAllowList is null || _helpPageSettings.HelpPageUrlAllowList.Contains(url); + private bool IsAllowedUrl(string? url) => url is null || _helpPageSettings.HelpPageUrlAllowList.Contains(url); } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Indexer/DetailsIndexerController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Indexer/DetailsIndexerController.cs index a6d8a7c407..0eee31be10 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Indexer/DetailsIndexerController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Indexer/DetailsIndexerController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Examine; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -34,11 +34,11 @@ public class DetailsIndexerController : IndexerControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(IndexResponseModel), StatusCodes.Status200OK)] - public async Task> Details(CancellationToken cancellationToken, string indexName) + public Task> Details(CancellationToken cancellationToken, string indexName) { if (_examineManager.TryGetIndex(indexName, out IIndex? index)) { - return await Task.FromResult(_indexPresentationFactory.Create(index!)); + return Task.FromResult>(_indexPresentationFactory.Create(index)); } var invalidModelProblem = new ProblemDetails @@ -49,7 +49,6 @@ public class DetailsIndexerController : IndexerControllerBase Type = "Error", }; - return await Task.FromResult(NotFound(invalidModelProblem)); - + return Task.FromResult>(NotFound(invalidModelProblem)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Indexer/RebuildIndexerController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Indexer/RebuildIndexerController.cs index 97131adcb9..cba2252991 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Indexer/RebuildIndexerController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Indexer/RebuildIndexerController.cs @@ -36,7 +36,7 @@ public class RebuildIndexerController : IndexerControllerBase [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task Rebuild(CancellationToken cancellationToken, string indexName) + public Task Rebuild(CancellationToken cancellationToken, string indexName) { if (!_examineManager.TryGetIndex(indexName, out IIndex? index)) { @@ -48,7 +48,7 @@ public class RebuildIndexerController : IndexerControllerBase Type = "Error", }; - return await Task.FromResult(NotFound(invalidModelProblem)); + return Task.FromResult(NotFound(invalidModelProblem)); } if (!_indexingRebuilderService.CanRebuild(index.Name)) @@ -62,14 +62,14 @@ public class RebuildIndexerController : IndexerControllerBase Type = "Error", }; - return await Task.FromResult(BadRequest(invalidModelProblem)); + return Task.FromResult(BadRequest(invalidModelProblem)); } _logger.LogInformation("Rebuilding index '{IndexName}'", indexName); if (_indexingRebuilderService.TryRebuild(index, indexName)) { - return await Task.FromResult(Ok()); + return Task.FromResult(Ok()); } var problemDetails = new ProblemDetails @@ -80,6 +80,6 @@ public class RebuildIndexerController : IndexerControllerBase Type = "Error", }; - return await Task.FromResult(Conflict(problemDetails)); + return Task.FromResult(Conflict(problemDetails)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/LogViewer/AllSinkLevelLogViewerController.cs b/src/Umbraco.Cms.Api.Management/Controllers/LogViewer/AllSinkLevelLogViewerController.cs index f2ec9e034f..e3bd052922 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/LogViewer/AllSinkLevelLogViewerController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/LogViewer/AllSinkLevelLogViewerController.cs @@ -30,7 +30,7 @@ public class AllSinkLevelLogViewerController : LogViewerControllerBase [HttpGet("level")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> AllLogLevels( + public Task>> AllLogLevels( CancellationToken cancellationToken, int skip = 0, int take = 100) @@ -45,6 +45,6 @@ public class AllSinkLevelLogViewerController : LogViewerControllerBase Items = _umbracoMapper.MapEnumerable, LoggerResponseModel>(logLevels.Skip(skip).Take(take)) }; - return await Task.FromResult(Ok(viewModel)); + return Task.FromResult>>(Ok(viewModel)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/Collection/ByKeyMediaCollectionController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/Collection/ByKeyMediaCollectionController.cs index 6a37c2ad40..ba6eb5c8b3 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/Collection/ByKeyMediaCollectionController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/Collection/ByKeyMediaCollectionController.cs @@ -1,12 +1,10 @@ using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.Media.Collection; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; @@ -22,20 +20,6 @@ public class ByKeyMediaCollectionController : MediaCollectionControllerBase private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; private readonly IMediaCollectionPresentationFactory _mediaCollectionPresentationFactory; - [Obsolete("Please use the constructor taking all parameters.")] - public ByKeyMediaCollectionController( - IMediaListViewService mediaListViewService, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IUmbracoMapper mapper) - : this( - mediaListViewService, - backOfficeSecurityAccessor, - mapper, - StaticServiceProvider.Instance.GetRequiredService()) - { - } - - [ActivatorUtilitiesConstructor] public ByKeyMediaCollectionController( IMediaListViewService mediaListViewService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/ConfigurationMediaController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/ConfigurationMediaController.cs index 0793dd64d3..9d61eac439 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/ConfigurationMediaController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/ConfigurationMediaController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -7,6 +7,7 @@ using Umbraco.Cms.Api.Management.ViewModels.Media; namespace Umbraco.Cms.Api.Management.Controllers.Media; [ApiVersion("1.0")] +[Obsolete("No longer used. Scheduled for removal in Umbraco 18.")] public class ConfigurationMediaController : MediaControllerBase { private readonly IConfigurationPresentationFactory _configurationPresentationFactory; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/ItemMediaItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/ItemMediaItemController.cs index ddea5cfbcb..7bab65c3ca 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/ItemMediaItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/ItemMediaItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -25,13 +25,13 @@ public class ItemMediaItemController : MediaItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable media = _entityService @@ -39,6 +39,6 @@ public class ItemMediaItemController : MediaItemControllerBase .OfType(); IEnumerable responseModels = media.Select(_mediaPresentationFactory.CreateItemResponseModel); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs index 6db5950179..b5da421abe 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -23,32 +23,21 @@ public class SearchMediaItemController : MediaItemControllerBase [NonAction] [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - public async Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) - => await SearchFromParent(cancellationToken, query, skip, take, null); - - [NonAction] - [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] - public async Task SearchFromParent(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, Guid? parentId = null) - => await SearchFromParentWithAllowedTypes(cancellationToken, query, skip, take, parentId); - - [NonAction] - [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - public async Task SearchFromParentWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, Guid? parentId = null, [FromQuery]IEnumerable? allowedMediaTypes = null) - => await SearchFromParentWithAllowedTypes(cancellationToken, query, null, skip, take, parentId); + public Task SearchFromParentWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, Guid? parentId = null, [FromQuery]IEnumerable? allowedMediaTypes = null) + => SearchFromParentWithAllowedTypes(cancellationToken, query, null, skip, take, parentId); [HttpGet("search")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] public async Task SearchFromParentWithAllowedTypes(CancellationToken cancellationToken, string query, bool? trashed = null, int skip = 0, int take = 100, Guid? parentId = null, [FromQuery]IEnumerable? allowedMediaTypes = null) { - PagedModel searchResult = _indexedEntitySearchService.Search(UmbracoObjectTypes.Media, query, parentId, allowedMediaTypes, trashed, skip, take); + PagedModel searchResult = await _indexedEntitySearchService.SearchAsync(UmbracoObjectTypes.Media, query, parentId, allowedMediaTypes, trashed, skip, take); var result = new PagedModel { Items = searchResult.Items.OfType().Select(_mediaPresentationFactory.CreateItemResponseModel), Total = searchResult.Total, }; - return await Task.FromResult(Ok(result)); + return Ok(result); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/MediaUrlController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/MediaUrlController.cs index d967bd9435..1db1a3c1be 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/MediaUrlController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/MediaUrlController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -25,10 +25,10 @@ public class MediaUrlController : MediaControllerBase [MapToApiVersion("1.0")] [HttpGet("urls")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task GetUrls([FromQuery(Name = "id")] HashSet ids) + public Task GetUrls([FromQuery(Name = "id")] HashSet ids) { IEnumerable items = _mediaService.GetByIds(ids); - return await Task.FromResult(Ok(_mediaUrlFactory.CreateUrlSets(items))); + return Task.FromResult(Ok(_mediaUrlFactory.CreateUrlSets(items))); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/RecycleBin/ReferencedByMediaRecycleBinController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/RecycleBin/ReferencedByMediaRecycleBinController.cs new file mode 100644 index 0000000000..a3c72184d7 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/RecycleBin/ReferencedByMediaRecycleBinController.cs @@ -0,0 +1,50 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.Media.RecycleBin; + +[ApiVersion("1.0")] +public class ReferencedByMediaRecycleBinController : MediaRecycleBinControllerBase +{ + private readonly ITrackedReferencesService _trackedReferencesService; + private readonly IRelationTypePresentationFactory _relationTypePresentationFactory; + + public ReferencedByMediaRecycleBinController( + IEntityService entityService, + IMediaPresentationFactory mediaPresentationFactory, + ITrackedReferencesService trackedReferencesService, + IRelationTypePresentationFactory relationTypePresentationFactory) + : base(entityService, mediaPresentationFactory) + { + _trackedReferencesService = trackedReferencesService; + _relationTypePresentationFactory = relationTypePresentationFactory; + } + + /// + /// Gets a paged list of tracked references for all items in the media recycle bin, so you can see where an item is being used. + /// + [HttpGet("referenced-by")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task>> ReferencedBy( + CancellationToken cancellationToken, + int skip = 0, + int take = 20) + { + PagedModel relationItems = await _trackedReferencesService.GetPagedRelationsForRecycleBinAsync(UmbracoObjectTypes.Media, skip, take, true); + + var pagedViewModel = new PagedViewModel + { + Total = relationItems.Total, + Items = await _relationTypePresentationFactory.CreateReferenceResponseModelsAsync(relationItems.Items), + }; + + return pagedViewModel; + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/AllowedChildrenMediaTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/AllowedChildrenMediaTypeController.cs index 71ee63d6e3..1981332c68 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/AllowedChildrenMediaTypeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/AllowedChildrenMediaTypeController.cs @@ -23,15 +23,6 @@ public class AllowedChildrenMediaTypeController : MediaTypeControllerBase _umbracoMapper = umbracoMapper; } - [NonAction] - [Obsolete("Use the non obsoleted method instead. Scheduled for removal in Umbraco 16.")] - public async Task AllowedChildrenByKey( - CancellationToken cancellationToken, - Guid id, - int skip = 0, - int take = 100) - => await AllowedChildrenByKey(cancellationToken, id, null, skip, take); - [HttpGet("{id:guid}/allowed-children")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/ByKeyMediaTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/ByKeyMediaTypeController.cs index ec3d955b2f..75a32569bd 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/ByKeyMediaTypeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/ByKeyMediaTypeController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.MediaType; @@ -34,6 +34,6 @@ public class ByKeyMediaTypeController : MediaTypeControllerBase } MediaTypeResponseModel model = _umbracoMapper.Map(mediaType)!; - return await Task.FromResult(Ok(model)); + return Ok(model); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/ItemMediaTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/ItemMediaTypeItemController.cs index 14bcff0e51..92133d2808 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/ItemMediaTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/ItemMediaTypeItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.MediaType.Item; @@ -23,17 +23,17 @@ public class ItemMediaTypeItemController : MediaTypeItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable mediaTypes = _mediaTypeService.GetMany(ids); List responseModels = _mapper.MapEnumerable(mediaTypes); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/SearchMediaTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/SearchMediaTypeItemController.cs index 475087766f..a6fbade1c7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/SearchMediaTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MediaType/Item/SearchMediaTypeItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.MediaType.Item; @@ -27,12 +27,12 @@ public class SearchMediaTypeItemController : MediaTypeItemControllerBase [HttpGet("search")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] - public async Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) + public Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) { PagedModel searchResult = _entitySearchService.Search(UmbracoObjectTypes.MediaType, query, skip, take); if (searchResult.Items.Any() is false) { - return await Task.FromResult(Ok(new PagedModel { Total = searchResult.Total })); + return Task.FromResult(Ok(new PagedModel { Total = searchResult.Total })); } IEnumerable mediaTypes = _mediaTypeService.GetMany(searchResult.Items.Select(item => item.Key).ToArray().EmptyNull()); @@ -42,6 +42,6 @@ public class SearchMediaTypeItemController : MediaTypeItemControllerBase Total = searchResult.Total }; - return Ok(result); + return Task.FromResult(Ok(result)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/ItemMemberItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/ItemMemberItemController.cs index eb81efed18..8e2e73538c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/ItemMemberItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/ItemMemberItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -25,13 +25,13 @@ public class ItemMemberItemController : MemberItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable members = _entityService @@ -39,6 +39,6 @@ public class ItemMemberItemController : MemberItemControllerBase .OfType(); IEnumerable responseModels = members.Select(_memberPresentationFactory.CreateItemResponseModel); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs index 187dc239b3..077bdf5278 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -21,15 +21,10 @@ public class SearchMemberItemController : MemberItemControllerBase _memberPresentationFactory = memberPresentationFactory; } - [NonAction] - [Obsolete("Scheduled to be removed in v16, use the non obsoleted method instead")] - public async Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) - => await SearchWithAllowedTypes(cancellationToken, query, skip, take); - [HttpGet("search")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] - public async Task SearchWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, [FromQuery]IEnumerable? allowedMemberTypes = null) + public Task SearchWithAllowedTypes(CancellationToken cancellationToken, string query, int skip = 0, int take = 100, [FromQuery]IEnumerable? allowedMemberTypes = null) { PagedModel searchResult = _indexedEntitySearchService.Search(UmbracoObjectTypes.Member, query, null, allowedMemberTypes, skip, take); var result = new PagedModel @@ -38,6 +33,6 @@ public class SearchMemberItemController : MemberItemControllerBase Total = searchResult.Total }; - return await Task.FromResult(Ok(result)); + return Task.FromResult(Ok(result)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/AllMemberGroupController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/AllMemberGroupController.cs index 8f2bfd7318..0f5b3384ba 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/AllMemberGroupController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/AllMemberGroupController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; @@ -36,6 +36,6 @@ public class AllMemberGroupController : MemberGroupControllerBase Items = _mapper.MapEnumerable(memberGroups.Skip(skip).Take(take)), }; - return await Task.FromResult(Ok(viewModel)); + return Ok(viewModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/Item/ItemMemberGroupItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/Item/ItemMemberGroupItemController.cs index 4a7b2e762d..479a8c5617 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/Item/ItemMemberGroupItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberGroup/Item/ItemMemberGroupItemController.cs @@ -25,17 +25,17 @@ public class ItemMemberGroupItemController : MemberGroupItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable memberGroups = _entityService.GetAll(UmbracoObjectTypes.MemberGroup, ids.ToArray()); List responseModel = _mapper.MapEnumerable(memberGroups); - return Ok(responseModel); + return Task.FromResult(Ok(responseModel)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/ItemMemberTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/ItemMemberTypeItemController.cs index c110e9ef6b..8aa28468b1 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/ItemMemberTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/ItemMemberTypeItemController.cs @@ -23,17 +23,17 @@ public class ItemMemberTypeItemController : MemberTypeItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } IEnumerable memberTypes = _memberTypeService.GetMany(ids); List responseModels = _mapper.MapEnumerable(memberTypes); - return Ok(responseModels); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/SearchMemberTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/SearchMemberTypeItemController.cs index 6e8151d8a2..0d211bad43 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/SearchMemberTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Item/SearchMemberTypeItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.MemberType.Item; @@ -26,12 +26,12 @@ public class SearchMemberTypeItemController : MemberTypeItemControllerBase [HttpGet("search")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] - public async Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) + public Task Search(CancellationToken cancellationToken, string query, int skip = 0, int take = 100) { PagedModel searchResult = _entitySearchService.Search(UmbracoObjectTypes.MemberType, query, skip, take); if (searchResult.Items.Any() is false) { - return await Task.FromResult(Ok(new PagedModel { Total = searchResult.Total })); + return Task.FromResult(Ok(new PagedModel { Total = searchResult.Total })); } IEnumerable memberTypes = _memberTypeService.GetMany(searchResult.Items.Select(item => item.Key).ToArray()); @@ -41,6 +41,6 @@ public class SearchMemberTypeItemController : MemberTypeItemControllerBase Total = searchResult.Total }; - return Ok(result); + return Task.FromResult(Ok(result)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/BuildModelsBuilderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/BuildModelsBuilderController.cs index 14adb70c3b..bf6c52fbb6 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/BuildModelsBuilderController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/BuildModelsBuilderController.cs @@ -19,7 +19,6 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase private readonly ModelsGenerationError _mbErrors; private readonly IModelsGenerator _modelGenerator; - [ActivatorUtilitiesConstructor] public BuildModelsBuilderController( IOptionsMonitor modelsBuilderSettings, ModelsGenerationError mbErrors, @@ -32,31 +31,11 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase modelsBuilderSettings.OnChange(x => _modelsBuilderSettings = x); } - [Obsolete("Please use the constructor that accepts IModelsGenerator only. Will be removed in V16.")] - public BuildModelsBuilderController( - IOptionsMonitor modelsBuilderSettings, - ModelsGenerationError mbErrors, - ModelsGenerator modelGenerator) - : this(modelsBuilderSettings, mbErrors, (IModelsGenerator)modelGenerator) - { - } - - // this constructor is required for the DI, otherwise it'll throw an "Ambiguous Constructor" errors at boot time. - [Obsolete("Please use the constructor that accepts IModelsGenerator only. Will be removed in V16.")] - public BuildModelsBuilderController( - IOptionsMonitor modelsBuilderSettings, - ModelsGenerationError mbErrors, - IModelsGenerator modelGenerator, - ModelsGenerator notUsed) - : this(modelsBuilderSettings, mbErrors, modelGenerator) - { - } - [HttpPost("build")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status428PreconditionRequired)] [MapToApiVersion("1.0")] - public async Task BuildModels(CancellationToken cancellationToken) + public Task BuildModels(CancellationToken cancellationToken) { try { @@ -70,7 +49,7 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase Type = "Error", }; - return new ObjectResult(problemDetailsModel) { StatusCode = StatusCodes.Status428PreconditionRequired }; + return Task.FromResult(new ObjectResult(problemDetailsModel) { StatusCode = StatusCodes.Status428PreconditionRequired }); } _modelGenerator.GenerateModels(); @@ -81,6 +60,6 @@ public class BuildModelsBuilderController : ModelsBuilderControllerBase _mbErrors.Report("Failed to build models.", e); } - return Ok(); + return Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/GetModelsBuilderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/GetModelsBuilderController.cs index 764ba65b75..492ad23c63 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/GetModelsBuilderController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/GetModelsBuilderController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -16,6 +16,6 @@ public class GetModelsBuilderController : ModelsBuilderControllerBase [HttpGet("dashboard")] [ProducesResponseType(typeof(ModelsBuilderResponseModel), StatusCodes.Status200OK)] [MapToApiVersion("1.0")] - public async Task> GetDashboard(CancellationToken cancellationToken) - => await Task.FromResult(Ok(_modelsBuilderPresentationFactory.Create())); + public Task> GetDashboard(CancellationToken cancellationToken) + => Task.FromResult>(Ok(_modelsBuilderPresentationFactory.Create())); } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/StatusModelsBuilderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/StatusModelsBuilderController.cs index fcbcd9e3cc..f49b16b851 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/StatusModelsBuilderController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/ModelsBuilder/StatusModelsBuilderController.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -18,7 +18,7 @@ public class StatusModelsBuilderController : ModelsBuilderControllerBase [HttpGet("status")] [ProducesResponseType(typeof(OutOfDateStatusResponseModel), StatusCodes.Status200OK)] [MapToApiVersion("1.0")] - public async Task> GetModelsOutOfDateStatus(CancellationToken cancellationToken) + public Task> GetModelsOutOfDateStatus(CancellationToken cancellationToken) { OutOfDateStatusResponseModel status = _outOfDateModelsStatus.IsEnabled ? _outOfDateModelsStatus.IsOutOfDate @@ -26,6 +26,6 @@ public class StatusModelsBuilderController : ModelsBuilderControllerBase : new OutOfDateStatusResponseModel { Status = OutOfDateType.Current } : new OutOfDateStatusResponseModel { Status = OutOfDateType.Unknown }; - return await Task.FromResult(Ok(status)); + return Task.FromResult>(Ok(status)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/ObjectTypes/AllowedObjectTypesController.cs b/src/Umbraco.Cms.Api.Management/Controllers/ObjectTypes/AllowedObjectTypesController.cs index 394ddb6573..7b5687971d 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/ObjectTypes/AllowedObjectTypesController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/ObjectTypes/AllowedObjectTypesController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; @@ -17,11 +17,11 @@ public class AllowedObjectTypesController : ObjectTypesControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task Allowed(CancellationToken cancellationToken, int skip = 0, int take = 100) + public Task Allowed(CancellationToken cancellationToken, int skip = 0, int take = 100) { ObjectTypeResponseModel[] objectTypes = _objectTypePresentationFactory.Create().ToArray(); - return await Task.FromResult(Ok(new PagedViewModel + return Task.FromResult(Ok(new PagedViewModel { Total = objectTypes.Length, Items = objectTypes.Skip(skip).Take(take), diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Package/Created/AllCreatedPackageController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Package/Created/AllCreatedPackageController.cs index 64ab3dec47..73ef803011 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Package/Created/AllCreatedPackageController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Package/Created/AllCreatedPackageController.cs @@ -44,6 +44,6 @@ public class AllCreatedPackageController : CreatedPackageControllerBase Items = _umbracoMapper.MapEnumerable(createdPackages.Items) }; - return await Task.FromResult(Ok(viewModel)); + return Ok(viewModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/PartialView/Item/ItemPartialViewItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/PartialView/Item/ItemPartialViewItemController.cs index 370f1ab8f5..27f09cb4d4 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/PartialView/Item/ItemPartialViewItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/PartialView/Item/ItemPartialViewItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Extensions; @@ -18,17 +18,17 @@ public class ItemPartialViewItemController : PartialViewItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "path")] HashSet paths) { if (paths.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } paths = paths.Select(path => path.VirtualPathToSystemPath()).ToHashSet(); IEnumerable responseModels = _fileItemPresentationFactory.CreatePartialViewItemResponseModels(paths); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/CollectPublishedCacheController.cs b/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/CollectPublishedCacheController.cs deleted file mode 100644 index ef759ca5a4..0000000000 --- a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/CollectPublishedCacheController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Asp.Versioning; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Umbraco.Cms.Api.Management.Controllers.PublishedCache; - -[Obsolete("This controller no longer serves a purpose")] -[ApiVersion("1.0")] -public class CollectPublishedCacheController : PublishedCacheControllerBase -{ - [HttpPost("collect")] - [MapToApiVersion("1.0")] - [ProducesResponseType(StatusCodes.Status501NotImplemented)] - public async Task Collect(CancellationToken cancellationToken) - { - return StatusCode(StatusCodes.Status501NotImplemented); - } -} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/RebuildPublishedCacheController.cs b/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/RebuildPublishedCacheController.cs index 8592f6722b..392c9338db 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/RebuildPublishedCacheController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/RebuildPublishedCacheController.cs @@ -21,8 +21,8 @@ public class RebuildPublishedCacheController : PublishedCacheControllerBase { var problemDetails = new ProblemDetails { - Title = "Database cache can not be rebuilt", - Detail = $"The database cache is in the process of rebuilding.", + Title = "Database cache cannot be rebuilt", + Detail = "The database cache is in the process of rebuilding.", Status = StatusCodes.Status400BadRequest, Type = "Error", }; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/ReloadPublishedCacheController.cs b/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/ReloadPublishedCacheController.cs index da655627f5..e147c9f775 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/ReloadPublishedCacheController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/ReloadPublishedCacheController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Cache; @@ -16,10 +16,9 @@ public class ReloadPublishedCacheController : PublishedCacheControllerBase [HttpPost("reload")] [MapToApiVersion("1.0")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task Reload(CancellationToken cancellationToken) + public Task Reload(CancellationToken cancellationToken) { _distributedCache.RefreshAllPublishedSnapshot(); - return await Task.FromResult(Ok()); + return Task.FromResult(Ok()); } } - diff --git a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/StatusPublishedCacheController.cs b/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/StatusPublishedCacheController.cs deleted file mode 100644 index 3a9d72c12c..0000000000 --- a/src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/StatusPublishedCacheController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Asp.Versioning; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Umbraco.Cms.Api.Management.Controllers.PublishedCache; - -[ApiVersion("1.0")] -[Obsolete("This no longer relevant since snapshots are no longer used")] -public class StatusPublishedCacheController : PublishedCacheControllerBase -{ - [HttpGet("status")] - [MapToApiVersion("1.0")] - [ProducesResponseType(StatusCodes.Status501NotImplemented)] - public async Task> Status(CancellationToken cancellationToken) - { - return StatusCode(StatusCodes.Status501NotImplemented); - } -} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/RecycleBin/RecycleBinControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/RecycleBin/RecycleBinControllerBase.cs index 69ae9b0b9d..850d7240d0 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/RecycleBin/RecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/RecycleBin/RecycleBinControllerBase.cs @@ -23,17 +23,18 @@ public abstract class RecycleBinControllerBase : ContentControllerBase protected abstract Guid RecycleBinRootKey { get; } - protected async Task>> GetRoot(int skip, int take) + protected Task>> GetRoot(int skip, int take) { IEntitySlim[] rootEntities = GetPagedRootEntities(skip, take, out var totalItems); TItem[] treeItemViewModels = MapRecycleBinViewModels(null, rootEntities); PagedViewModel result = PagedViewModel(treeItemViewModels, totalItems); - return await Task.FromResult(Ok(result)); + + return Task.FromResult>>(Ok(result)); } - protected async Task>> GetChildren(Guid parentKey, int skip, int take) + protected Task>> GetChildren(Guid parentKey, int skip, int take) { IEntitySlim[] children = GetPagedChildEntities(parentKey, skip, take, out var totalItems); @@ -41,7 +42,7 @@ public abstract class RecycleBinControllerBase : ContentControllerBase PagedViewModel result = PagedViewModel(treeItemViewModels, totalItems); - return await Task.FromResult(Ok(result)); + return Task.FromResult>>(Ok(result)); } protected virtual TItem MapRecycleBinViewModel(Guid? parentKey, IEntitySlim entity) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/DeleteByKeyRedirectUrlManagementController.cs b/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/DeleteByKeyRedirectUrlManagementController.cs index 3a0e60b61d..6f33b16699 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/DeleteByKeyRedirectUrlManagementController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/DeleteByKeyRedirectUrlManagementController.cs @@ -18,9 +18,9 @@ public class DeleteByKeyRedirectUrlManagementController : RedirectUrlManagementC [MapToApiVersion("1.0")] [HttpDelete("{id:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task DeleteByKey(CancellationToken cancellationToken, Guid id) + public Task DeleteByKey(CancellationToken cancellationToken, Guid id) { _redirectUrlService.Delete(id); - return Ok(); + return Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/GetAllRedirectUrlManagementController.cs b/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/GetAllRedirectUrlManagementController.cs index f4600ef653..bd3449679e 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/GetAllRedirectUrlManagementController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/RedirectUrlManagement/GetAllRedirectUrlManagementController.cs @@ -27,7 +27,7 @@ public class GetAllRedirectUrlManagementController : RedirectUrlManagementContro [HttpGet] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> GetAll( + public Task>> GetAll( CancellationToken cancellationToken, string? filter, int skip = 0, @@ -39,6 +39,6 @@ public class GetAllRedirectUrlManagementController : RedirectUrlManagementContro : _redirectUrlService.SearchRedirectUrls(filter, skip, take, out total); IEnumerable redirectViewModels = _redirectUrlPresentationFactory.CreateMany(redirects); - return new PagedViewModel { Items = redirectViewModels, Total = total }; + return Task.FromResult>>(new PagedViewModel { Items = redirectViewModels, Total = total }); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs index cde52b5884..189019cc85 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Relation/ByRelationTypeKeyRelationController.cs @@ -43,16 +43,15 @@ public class ByRelationTypeKeyRelationController : RelationControllerBase if (relationsAttempt.Success is false) { - return await Task.FromResult(RelationOperationStatusResult(relationsAttempt.Status)); + return RelationOperationStatusResult(relationsAttempt.Status); } IEnumerable mappedRelations = relationsAttempt.Result.Items.Select(_relationPresentationFactory.Create); - return await Task.FromResult(Ok(new PagedViewModel + return Ok(new PagedViewModel { Total = relationsAttempt.Result.Total, Items = mappedRelations, - })); - + }); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/RelationType/ByKeyRelationTypeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/RelationType/ByKeyRelationTypeController.cs index a0d6ae4a46..691c6ae0ef 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/RelationType/ByKeyRelationTypeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/RelationType/ByKeyRelationTypeController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.RelationType; @@ -24,16 +24,16 @@ public class ByKeyRelationTypeController : RelationTypeControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(RelationTypeResponseModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - public async Task ByKey(CancellationToken cancellationToken, Guid id) + public Task ByKey(CancellationToken cancellationToken, Guid id) { IRelationType? relationType = _relationService.GetRelationTypeById(id); if (relationType is null) { - return RelationTypeNotFound(); + return Task.FromResult(RelationTypeNotFound()); } RelationTypeResponseModel mappedRelationType = _mapper.Map(relationType)!; - return await Task.FromResult(Ok(mappedRelationType)); + return Task.FromResult(Ok(mappedRelationType)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Item/ItemRelationTypeItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Item/ItemRelationTypeItemController.cs index 6745108b93..6ba42c1579 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Item/ItemRelationTypeItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/RelationType/Item/ItemRelationTypeItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.RelationType.Item; @@ -23,13 +23,13 @@ public class ItemRelationTypeItemController : RelationTypeItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "id")] HashSet ids) { if (ids.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } // relation service does not allow fetching a collection of relation types by their ids; instead it relies @@ -40,6 +40,6 @@ public class ItemRelationTypeItemController : RelationTypeItemControllerBase List responseModels = _mapper.MapEnumerable(relationTypes); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Script/Item/ItemScriptItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Script/Item/ItemScriptItemController.cs index 5786c7aaf6..e18eda31a7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Script/Item/ItemScriptItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Script/Item/ItemScriptItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Extensions; @@ -18,17 +18,17 @@ public class ItemScriptItemController : ScriptItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "path")] HashSet paths) { if (paths.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } paths = paths.Select(path => path.VirtualPathToSystemPath()).ToHashSet(); IEnumerable responseModels = _fileItemPresentationFactory.CreateScriptItemResponseModels(paths); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Searcher/AllSearcherController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Searcher/AllSearcherController.cs index 76cd2f639b..60ee9f22b7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Searcher/AllSearcherController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Searcher/AllSearcherController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Examine; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -22,7 +22,7 @@ public class AllSearcherController : SearcherControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task>> All( + public Task>> All( CancellationToken cancellationToken, int skip = 0, int take = 100) @@ -37,6 +37,6 @@ public class AllSearcherController : SearcherControllerBase Total = searchers.Count, }; - return await Task.FromResult(Ok(viewModel)); + return Task.FromResult>>(Ok(viewModel)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Searcher/QuerySearcherController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Searcher/QuerySearcherController.cs index f3f7d0acad..2017d4b376 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Searcher/QuerySearcherController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Searcher/QuerySearcherController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Examine; using Examine.Lucene.Search; using Examine.Search; @@ -23,7 +23,7 @@ public class QuerySearcherController : SearcherControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - public async Task>> Query( + public Task>> Query( CancellationToken cancellationToken, string searcherName, string? term, @@ -34,7 +34,7 @@ public class QuerySearcherController : SearcherControllerBase if (term.IsNullOrWhiteSpace()) { - return new PagedViewModel(); + return Task.FromResult>>(new PagedViewModel()); } if (!_examineManagerService.TryFindSearcher(searcherName, out ISearcher searcher)) @@ -47,7 +47,7 @@ public class QuerySearcherController : SearcherControllerBase Type = "Error", }; - return NotFound(invalidModelProblem); + return Task.FromResult>>(NotFound(invalidModelProblem)); } ISearchResults results; @@ -71,10 +71,10 @@ public class QuerySearcherController : SearcherControllerBase Type = "Error", }; - return BadRequest(invalidModelProblem); + return Task.FromResult>>(BadRequest(invalidModelProblem)); } - return await Task.FromResult(new PagedViewModel + return Task.FromResult>>(new PagedViewModel { Total = results.TotalItemCount, Items = results.Select(x => new SearchResultResponseModel diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs index cada1031e3..de2ae97a2d 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs @@ -492,7 +492,7 @@ public class BackOfficeController : SecurityControllerBase return new ChallengeResult(provider, properties); } - private async Task SignInBackOfficeUser(ClaimsPrincipal backOfficePrincipal, OpenIddictRequest request) + private Task SignInBackOfficeUser(ClaimsPrincipal backOfficePrincipal, OpenIddictRequest request) { Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray(); foreach (Claim backOfficeClaim in backOfficeClaims) @@ -506,7 +506,7 @@ public class BackOfficeController : SecurityControllerBase backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess); } - return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal); + return Task.FromResult(new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal)); } private async Task SignInBackOfficeUser(BackOfficeIdentityUser backOfficeUser, OpenIddictRequest request) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs index e65ea9752c..c96f4a2bcf 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/ConfigurationSecurityController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -20,13 +20,13 @@ public class ConfigurationSecurityController : SecurityControllerBase [HttpGet("configuration")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(SecurityConfigurationResponseModel), StatusCodes.Status200OK)] - public async Task Configuration(CancellationToken cancellationToken) + public Task Configuration(CancellationToken cancellationToken) { var viewModel = new SecurityConfigurationResponseModel { PasswordConfiguration = _passwordConfigurationPresentationFactory.CreatePasswordConfigurationResponseModel(), }; - return await Task.FromResult(Ok(viewModel)); + return Task.FromResult(Ok(viewModel)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs index 09b493c484..1482517a15 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs @@ -21,6 +21,10 @@ public abstract class SecurityControllerBase : ManagementApiControllerBase .WithTitle("The password reset token was invalid") .WithDetail("The specified password reset token was either used already or wrong.") .Build()), + UserOperationStatus.CancelledByNotification => BadRequest(problemDetailsBuilder + .WithTitle("Cancelled by notification") + .WithDetail("A notification handler prevented the user operation.") + .Build()), UserOperationStatus.UnknownFailure => BadRequest(problemDetailsBuilder .WithTitle("Unknown failure") .WithDetail(errorMessageResult?.Error?.ErrorMessage ?? "The error was unknown") diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Server/ConfigurationServerController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Server/ConfigurationServerController.cs index 05c35f32cb..48a5c7d288 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Server/ConfigurationServerController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Server/ConfigurationServerController.cs @@ -1,13 +1,11 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Api.Management.Security; using Umbraco.Cms.Api.Management.ViewModels.Server; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; namespace Umbraco.Cms.Api.Management.Controllers.Server; @@ -18,19 +16,6 @@ public class ConfigurationServerController : ServerControllerBase private readonly GlobalSettings _globalSettings; private readonly IBackOfficeExternalLoginProviders _externalLoginProviders; - [Obsolete("Use the constructor that accepts all arguments. Will be removed in V16.")] - public ConfigurationServerController(IOptions securitySettings) - : this(securitySettings, StaticServiceProvider.Instance.GetRequiredService>()) - { - } - - [Obsolete("Use the constructor that accepts all arguments. Will be removed in V16.")] - public ConfigurationServerController(IOptions securitySettings, IOptions globalSettings) - : this(securitySettings, globalSettings, StaticServiceProvider.Instance.GetRequiredService()) - { - } - - [ActivatorUtilitiesConstructor] public ConfigurationServerController(IOptions securitySettings, IOptions globalSettings, IBackOfficeExternalLoginProviders externalLoginProviders) { _securitySettings = securitySettings.Value; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs index 0e25c9d3c6..4a9afe24ec 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -19,6 +19,6 @@ public class StatusServerController : ServerControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ServerStatusResponseModel), StatusCodes.Status200OK)] - public async Task> Get(CancellationToken cancellationToken) => - await Task.FromResult(new ServerStatusResponseModel { ServerStatus = _runtimeState.Level }); + public Task> Get(CancellationToken cancellationToken) + => Task.FromResult>(new ServerStatusResponseModel { ServerStatus = _runtimeState.Level }); } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/StaticFile/Item/ItemStaticFileItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/StaticFile/Item/ItemStaticFileItemController.cs index 3301ecf8e1..600499c8a1 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/StaticFile/Item/ItemStaticFileItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/StaticFile/Item/ItemStaticFileItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Extensions; @@ -18,17 +18,17 @@ public class ItemStaticFileItemController : StaticFileItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "path")] HashSet paths) { if (paths.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } paths = paths.Select(path => path.VirtualPathToSystemPath()).ToHashSet(); IEnumerable responseModels = _fileItemPresentationFactory.CreateStaticFileItemResponseModels(paths); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Stylesheet/Item/ItemStylesheetItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Stylesheet/Item/ItemStylesheetItemController.cs index d9ab9b4aa9..f5e6d71adb 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Stylesheet/Item/ItemStylesheetItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Stylesheet/Item/ItemStylesheetItemController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Extensions; @@ -18,17 +18,17 @@ public class ItemStylesheetItemController : StylesheetItemControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Item( + public Task Item( CancellationToken cancellationToken, [FromQuery(Name = "path")] HashSet paths) { if (paths.Count is 0) { - return Ok(Enumerable.Empty()); + return Task.FromResult(Ok(Enumerable.Empty())); } paths = paths.Select(path => path.VirtualPathToSystemPath()).ToHashSet(); IEnumerable responseModels = _fileItemPresentationFactory.CreateStylesheetItemResponseModels(paths); - return await Task.FromResult(Ok(responseModels)); + return Task.FromResult(Ok(responseModels)); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Tag/ByQueryTagController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Tag/ByQueryTagController.cs index d528eabbc4..368d908598 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Tag/ByQueryTagController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Tag/ByQueryTagController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; @@ -42,6 +42,6 @@ public class ByQueryTagController : TagControllerBase Total = responseModels.Count, }; - return await Task.FromResult(Ok(pagedViewModel)); + return Ok(pagedViewModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/AllTelemetryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/AllTelemetryController.cs index 625b8b146b..df18521fbc 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/AllTelemetryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/AllTelemetryController.cs @@ -13,13 +13,13 @@ public class AllTelemetryController : TelemetryControllerBase [HttpGet] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> GetAll( + public Task> GetAll( CancellationToken cancellationToken, int skip = 0, int take = 100) { TelemetryLevel[] levels = Enum.GetValues(); - return await Task.FromResult(new PagedViewModel + return Task.FromResult(new PagedViewModel { Total = levels.Length, Items = levels.Skip(skip).Take(take).Select(level => new TelemetryResponseModel { TelemetryLevel = level }), diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/GetTelemetryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/GetTelemetryController.cs index d3bfe107a3..c442e6727b 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/GetTelemetryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/GetTelemetryController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Services; @@ -16,6 +16,6 @@ public class GetTelemetryController : TelemetryControllerBase [HttpGet("level")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(TelemetryResponseModel), StatusCodes.Status200OK)] - public async Task Get(CancellationToken cancellationToken) - => await Task.FromResult(new TelemetryResponseModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); + public Task Get(CancellationToken cancellationToken) + => Task.FromResult(new TelemetryResponseModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs index b0f8110e40..f48abbc11d 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Services; @@ -34,6 +34,6 @@ public class SetTelemetryController : TelemetryControllerBase } await _metricsConsentService.SetConsentLevelAsync(telemetryRepresentationBase.TelemetryLevel); - return await Task.FromResult(Ok()); + return Ok(); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs index 3634b3fa1c..07063b69f7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs @@ -81,7 +81,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase [HttpPost("execute")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(TemplateQueryResultResponseModel), StatusCodes.Status200OK)] - public async Task> Execute( + public Task> Execute( CancellationToken cancellationToken, TemplateQueryExecuteModel query) { @@ -97,7 +97,7 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase .GetMany(results.Select(content => content.ContentType.Key).Distinct()) .ToDictionary(contentType => contentType.Key, contentType => contentType.Icon); - return await Task.FromResult(Ok(new TemplateQueryResultResponseModel + return Task.FromResult>(Ok(new TemplateQueryResultResponseModel { QueryExpression = queryExpression.ToString(), ResultCount = results.Count, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/SettingsTemplateQueryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/SettingsTemplateQueryController.cs index d5d5026c4f..868bd8baa3 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/SettingsTemplateQueryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/SettingsTemplateQueryController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.ViewModels.Template.Query; @@ -17,7 +17,7 @@ public class SettingsTemplateQueryController : TemplateQueryControllerBase [HttpGet("settings")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(TemplateQuerySettingsResponseModel), StatusCodes.Status200OK)] - public async Task> Settings(CancellationToken cancellationToken) + public Task> Settings(CancellationToken cancellationToken) { var contentTypeAliases = _contentTypeService .GetAll() @@ -29,7 +29,7 @@ public class SettingsTemplateQueryController : TemplateQueryControllerBase IEnumerable operators = GetOperators(); - return await Task.FromResult(Ok(new TemplateQuerySettingsResponseModel + return Task.FromResult>(Ok(new TemplateQuerySettingsResponseModel { DocumentTypeAliases = contentTypeAliases, Properties = properties, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/TemporaryFile/TemporaryFileControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/TemporaryFile/TemporaryFileControllerBase.cs index b33f63d7ac..ac808918fe 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/TemporaryFile/TemporaryFileControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/TemporaryFile/TemporaryFileControllerBase.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.Builders; using Umbraco.Cms.Api.Management.Routing; @@ -17,6 +17,9 @@ public abstract class TemporaryFileControllerBase : ManagementApiControllerBase .WithTitle("File extension not allowed") .WithDetail("The file extension is not allowed.") .Build()), + TemporaryFileOperationStatus.InvalidFileName => BadRequest(problemDetailsBuilder + .WithTitle("The provided file name is not valid") + .Build()), TemporaryFileOperationStatus.KeyAlreadyUsed => BadRequest(problemDetailsBuilder .WithTitle("Key already used") .WithDetail("The specified key is already used.") diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Tree/EntityTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Tree/EntityTreeControllerBase.cs index 57f575fd8a..bf86b852ed 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Tree/EntityTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Tree/EntityTreeControllerBase.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.Tree; @@ -22,24 +22,26 @@ public abstract class EntityTreeControllerBase : ManagementApiControllerB protected virtual Ordering ItemOrdering => Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.Text)); - protected async Task>> GetRoot(int skip, int take) + protected Task>> GetRoot(int skip, int take) { IEntitySlim[] rootEntities = GetPagedRootEntities(skip, take, out var totalItems); TItem[] treeItemViewModels = MapTreeItemViewModels(null, rootEntities); PagedViewModel result = PagedViewModel(treeItemViewModels, totalItems); - return await Task.FromResult(Ok(result)); + + return Task.FromResult>>(Ok(result)); } - protected async Task>> GetChildren(Guid parentId, int skip, int take) + protected Task>> GetChildren(Guid parentId, int skip, int take) { IEntitySlim[] children = GetPagedChildEntities(parentId, skip, take, out var totalItems); TItem[] treeItemViewModels = MapTreeItemViewModels(parentId, children); PagedViewModel result = PagedViewModel(treeItemViewModels, totalItems); - return await Task.FromResult(Ok(result)); + + return Task.FromResult>>(Ok(result)); } protected virtual async Task>> GetAncestors(Guid descendantKey, bool includeSelf = true) @@ -60,13 +62,13 @@ public abstract class EntityTreeControllerBase : ManagementApiControllerB return Ok(result); } - protected virtual async Task GetAncestorEntitiesAsync(Guid descendantKey, bool includeSelf) + protected virtual Task GetAncestorEntitiesAsync(Guid descendantKey, bool includeSelf) { IEntitySlim? entity = EntityService.Get(descendantKey, ItemObjectType); if (entity is null) { // not much else we can do here but return nothing - return await Task.FromResult(Array.Empty()); + return Task.FromResult(Array.Empty()); } var ancestorIds = entity.AncestorIds(); @@ -76,7 +78,7 @@ public abstract class EntityTreeControllerBase : ManagementApiControllerB : Array.Empty(); ancestors = ancestors.Union(includeSelf ? new[] { entity } : Array.Empty()); - return ancestors.OrderBy(item => item.Level).ToArray(); + return Task.FromResult(ancestors.OrderBy(item => item.Level).ToArray()); } protected virtual IEntitySlim[] GetPagedRootEntities(int skip, int take, out long totalItems) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Tree/FileSystemTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Tree/FileSystemTreeControllerBase.cs index 80b1edffeb..1388e4b798 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Tree/FileSystemTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Tree/FileSystemTreeControllerBase.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.ViewModels.Pagination; using Umbraco.Cms.Api.Management.Extensions; using Umbraco.Cms.Api.Management.ViewModels.FileSystem; @@ -12,28 +12,28 @@ public abstract class FileSystemTreeControllerBase : ManagementApiControllerBase { protected abstract IFileSystem FileSystem { get; } - protected async Task>> GetRoot(int skip, int take) + protected Task>> GetRoot(int skip, int take) { FileSystemTreeItemPresentationModel[] viewModels = GetPathViewModels(string.Empty, skip, take, out var totalItems); PagedViewModel result = PagedViewModel(viewModels, totalItems); - return await Task.FromResult(Ok(result)); + return Task.FromResult>>(Ok(result)); } - protected async Task>> GetChildren(string path, int skip, int take) + protected Task>> GetChildren(string path, int skip, int take) { FileSystemTreeItemPresentationModel[] viewModels = GetPathViewModels(path, skip, take, out var totalItems); PagedViewModel result = PagedViewModel(viewModels, totalItems); - return await Task.FromResult(Ok(result)); + return Task.FromResult>>(Ok(result)); } - protected virtual async Task>> GetAncestors(string path, bool includeSelf = true) + protected virtual Task>> GetAncestors(string path, bool includeSelf = true) { path = path.VirtualPathToSystemPath(); FileSystemTreeItemPresentationModel[] models = GetAncestorModels(path, includeSelf); - return await Task.FromResult(Ok(models)); + return Task.FromResult>>(Ok(models)); } protected virtual FileSystemTreeItemPresentationModel[] GetAncestorModels(string path, bool includeSelf) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs index 2a33d95aa3..a7c201372c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Tree/FolderTreeControllerBase.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Api.Management.ViewModels.Tree; +using Umbraco.Cms.Api.Management.ViewModels.Tree; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -63,14 +63,14 @@ public abstract class FolderTreeControllerBase : NamedEntityTreeControlle return viewModel; } - protected override async Task GetAncestorEntitiesAsync(Guid descendantKey, bool includeSelf = true) + protected override Task GetAncestorEntitiesAsync(Guid descendantKey, bool includeSelf = true) { IEntitySlim? entity = EntityService.Get(descendantKey, ItemObjectType) ?? EntityService.Get(descendantKey, FolderObjectType); if (entity is null) { // not much else we can do here but return nothing - return await Task.FromResult(Array.Empty()); + return Task.FromResult(Array.Empty()); } var ancestorIds = entity.AncestorIds(); @@ -81,9 +81,12 @@ public abstract class FolderTreeControllerBase : NamedEntityTreeControlle .GetAll(ItemObjectType, ancestorIds) .Union(containers) : Array.Empty(); - ancestors = ancestors.Union(includeSelf ? new[] { entity } : Array.Empty()); + if (includeSelf) + { + ancestors = ancestors.Append(entity); + } - return ancestors.OrderBy(item => item.Level).ToArray(); + return Task.FromResult(ancestors.OrderBy(item => item.Level).ToArray()); } private IEntitySlim[] GetEntities(Guid? parentKey, int skip, int take, out long totalItems) @@ -94,7 +97,7 @@ public abstract class FolderTreeControllerBase : NamedEntityTreeControlle IEntitySlim[] itemEntities = EntityService.GetPagedChildren( parentKey, - new [] { FolderObjectType, ItemObjectType }, + [FolderObjectType, ItemObjectType], childObjectTypes, skip, take, diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Tree/UserStartNodeTreeControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Tree/UserStartNodeTreeControllerBase.cs index d7a803b584..8c15708f1f 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Tree/UserStartNodeTreeControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Tree/UserStartNodeTreeControllerBase.cs @@ -42,11 +42,21 @@ public abstract class UserStartNodeTreeControllerBase : EntityTreeControl protected override IEntitySlim[] GetPagedChildEntities(Guid parentKey, int skip, int take, out long totalItems) { - IEntitySlim[] children = base.GetPagedChildEntities(parentKey, skip, take, out totalItems); - return UserHasRootAccess() || IgnoreUserStartNodes() - ? children - // Keeping the correct totalItems amount from GetPagedChildEntities - : CalculateAccessMap(() => _userStartNodeEntitiesService.ChildUserAccessEntities(children, UserStartNodePaths), out _); + if (UserHasRootAccess() || IgnoreUserStartNodes()) + { + return base.GetPagedChildEntities(parentKey, skip, take, out totalItems); + } + + IEnumerable userAccessEntities = _userStartNodeEntitiesService.ChildUserAccessEntities( + ItemObjectType, + UserStartNodePaths, + parentKey, + skip, + take, + ItemOrdering, + out totalItems); + + return CalculateAccessMap(() => userAccessEntities, out _); } protected override TItem[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Upgrade/SettingsUpgradeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Upgrade/SettingsUpgradeController.cs index 66e212650f..2dcbd8a014 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Upgrade/SettingsUpgradeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Upgrade/SettingsUpgradeController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Mapping; @@ -26,7 +26,7 @@ public class SettingsUpgradeController : UpgradeControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(UpgradeSettingsResponseModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status428PreconditionRequired)] - public async Task> Settings(CancellationToken cancellationToken) + public Task> Settings(CancellationToken cancellationToken) { // TODO: Async - We need to figure out what we want to do with async endpoints that doesn't do anything async // We want these to be async for future use (Ideally we'll have more async things), @@ -34,6 +34,6 @@ public class SettingsUpgradeController : UpgradeControllerBase UpgradeSettingsModel upgradeSettings = _upgradeSettingsFactory.GetUpgradeSettings(); UpgradeSettingsResponseModel responseModel = _mapper.Map(upgradeSettings)!; - return await Task.FromResult(responseModel); + return Task.FromResult>(responseModel); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs b/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs index 2c2dba9a19..ea13c080e2 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/UrlSegment/ResizeImagingController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; @@ -23,10 +23,10 @@ public class ResizeImagingController : ImagingControllerBase [MapToApiVersion("1.0")] [HttpGet("resize/urls")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] - public async Task Urls([FromQuery(Name = "id")] HashSet ids, int height = 200, int width = 200, ImageCropMode? mode = null) + public Task Urls([FromQuery(Name = "id")] HashSet ids, int height = 200, int width = 200, ImageCropMode? mode = null) { IEnumerable items = _mediaService.GetByIds(ids); - return await Task.FromResult(Ok(_reziseImageUrlFactory.CreateUrlSets(items, height, width, mode))); + return Task.FromResult(Ok(_reziseImageUrlFactory.CreateUrlSets(items, height, width, mode))); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/ConfigurationUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/ConfigurationUserController.cs index 7c9723c744..915158a92c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/ConfigurationUserController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/ConfigurationUserController.cs @@ -1,15 +1,12 @@ -using Asp.Versioning; -using Microsoft.AspNetCore.Authorization; +using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Management.Factories; using Umbraco.Cms.Api.Management.ViewModels.User; -using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers.User; [ApiVersion("1.0")] -[Authorize(Policy = AuthorizationPolicies.RequireAdminAccess)] public class ConfigurationUserController : UserControllerBase { private readonly IUserPresentationFactory _userPresentationFactory; diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/Current/ConfigurationCurrentUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/Current/ConfigurationCurrentUserController.cs index 5d368c42c6..6e93a1524f 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/Current/ConfigurationCurrentUserController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/Current/ConfigurationCurrentUserController.cs @@ -1,4 +1,4 @@ -using Asp.Versioning; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -18,10 +18,10 @@ public class ConfigurationCurrentUserController : CurrentUserControllerBase [MapToApiVersion("1.0")] [HttpGet("configuration")] - [ProducesResponseType(typeof(CurrenUserConfigurationResponseModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(CurrentUserConfigurationResponseModel), StatusCodes.Status200OK)] public async Task Configuration(CancellationToken cancellationToken) { - CurrenUserConfigurationResponseModel model = await _userPresentationFactory.CreateCurrentUserConfigurationModelAsync(); + CurrentUserConfigurationResponseModel model = await _userPresentationFactory.CreateCurrentUserConfigurationModelAsync(); return Ok(model); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/UserGroup/UserGroupControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/UserGroup/UserGroupControllerBase.cs index a5691932b1..0112df8127 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/UserGroup/UserGroupControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/UserGroup/UserGroupControllerBase.cs @@ -54,8 +54,12 @@ public class UserGroupControllerBase : ManagementApiControllerBase .WithDetail("The assigned media start node does not exists.") .Build()), UserGroupOperationStatus.DocumentPermissionKeyNotFound => NotFound(new ProblemDetailsBuilder() - .WithTitle("A document permission key not found") - .WithDetail("A assigned document permission not exists.") + .WithTitle("Document permission key not found") + .WithDetail("An assigned document permission does not reference an existing document.") + .Build()), + UserGroupOperationStatus.DocumentTypePermissionKeyNotFound => NotFound(new ProblemDetailsBuilder() + .WithTitle("Document type permission key not found") + .WithDetail("An assigned document type permission does not reference an existing document type.") .Build()), UserGroupOperationStatus.LanguageNotFound => NotFound(problemDetailsBuilder .WithTitle("Language not found") diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/ApplicationBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/ApplicationBuilderExtensions.cs index 870c4d3e1e..7a3394ad5d 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/ApplicationBuilderExtensions.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -6,9 +6,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Extensions; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; @@ -20,14 +19,10 @@ internal static class ApplicationBuilderExtensions => applicationBuilder.UseWhen( httpContext => { - GlobalSettings settings = httpContext.RequestServices - .GetRequiredService>().Value; - IHostingEnvironment hostingEnvironment = - httpContext.RequestServices.GetRequiredService(); - var officePath = settings.GetBackOfficePath(hostingEnvironment); + var backOfficePath = httpContext.RequestServices.GetRequiredService().GetBackOfficePath(); // Only use the API exception handler when we are requesting an API - return httpContext.Request.Path.Value?.StartsWith($"{officePath}{Constants.Web.ManagementApiPath}") ?? false; + return httpContext.Request.Path.Value?.StartsWith($"{backOfficePath}{Constants.Web.ManagementApiPath}") ?? false; }, innerBuilder => { @@ -58,14 +53,13 @@ internal static IApplicationBuilder UseEndpoints(this IApplicationBuilder applic applicationBuilder.UseEndpoints(endpoints => { - GlobalSettings settings = provider.GetRequiredService>().Value; - IHostingEnvironment hostingEnvironment = provider.GetRequiredService(); - var officePath = settings.GetBackOfficePath(hostingEnvironment); + var backOfficePath = provider.GetRequiredService().GetBackOfficePath(); + // Maps attribute routed controllers. endpoints.MapControllers(); // Serve contract - endpoints.MapGet($"{officePath}{Constants.Web.ManagementApiPath}openapi.json", async context => + endpoints.MapGet($"{backOfficePath}{Constants.Web.ManagementApiPath}openapi.json", async context => { await context.Response.SendFileAsync(new EmbeddedFileProvider(typeof(ManagementApiComposer).Assembly).GetFileInfo("OpenApi.json")); }); diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/SearchManagementBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/SearchManagementBuilderExtensions.cs index e229d51ee9..5852907b88 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/SearchManagementBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/SearchManagementBuilderExtensions.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Api.Management.Factories; @@ -17,8 +17,8 @@ public static class SearchManagementBuilderExtensions // Add factories builder.Services.AddTransient(); - builder.Services.AddTransient(); builder.Services.AddTransient(); + return builder; } } diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs index c8114a6384..2b3e4e06e6 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/UmbracoBuilder.BackOffice.cs @@ -32,13 +32,10 @@ public static partial class UmbracoBuilderExtensions .AddMembersIdentity() .AddUmbracoProfiler() .AddMvcAndRazor(configureMvc) - .AddWebServer() .AddRecurringBackgroundJobs() .AddUmbracoHybridCache() .AddDistributedCache() - .AddCoreNotifications() - .AddExamine() - .AddExamineIndexes(); + .AddCoreNotifications(); public static IUmbracoBuilder AddBackOfficeCore(this IUmbracoBuilder builder) { diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/UserGroupsBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/UserGroupsBuilderExtensions.cs index b7902e490a..5252151573 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/UserGroupsBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/UserGroupsBuilderExtensions.cs @@ -13,10 +13,11 @@ internal static class UserGroupsBuilderExtensions builder.Services.AddTransient(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(x=>x.GetRequiredService()); - builder.Services.AddSingleton(x=>x.GetRequiredService()); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); return builder; } diff --git a/src/Umbraco.Cms.Api.Management/Factories/ConfigurationPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/ConfigurationPresentationFactory.cs index 7efc4ff90f..31c10ae4ca 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/ConfigurationPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/ConfigurationPresentationFactory.cs @@ -1,4 +1,3 @@ -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Api.Management.ViewModels.DocumentType; @@ -7,7 +6,6 @@ using Umbraco.Cms.Api.Management.ViewModels.MediaType; using Umbraco.Cms.Api.Management.ViewModels.Member; using Umbraco.Cms.Api.Management.ViewModels.MemberType; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Features; using Umbraco.Cms.Core.Services; @@ -35,21 +33,6 @@ public class ConfigurationPresentationFactory : IConfigurationPresentationFactor _segmentSettings = segmentSettings.Value; } - [Obsolete("Use the constructor with all dependencies")] - public ConfigurationPresentationFactory( - IReservedFieldNamesService reservedFieldNamesService, - IOptions contentSettings, - IOptions segmentSettings) - : this( - reservedFieldNamesService, - contentSettings, - segmentSettings, - StaticServiceProvider.Instance.GetRequiredService>(), - StaticServiceProvider.Instance.GetRequiredService() - ) - { - } - public DocumentConfigurationResponseModel CreateDocumentConfigurationResponseModel() => new() { @@ -57,7 +40,6 @@ public class ConfigurationPresentationFactory : IConfigurationPresentationFactor DisableUnpublishWhenReferenced = _contentSettings.DisableUnpublishWhenReferenced, AllowEditInvariantFromNonDefault = _contentSettings.AllowEditInvariantFromNonDefault, AllowNonExistingSegmentsCreation = _segmentSettings.AllowCreation, - ReservedFieldNames = _reservedFieldNamesService.GetDocumentReservedFieldNames(), }; public DocumentTypeConfigurationResponseModel CreateDocumentTypeConfigurationResponseModel() => @@ -70,10 +52,7 @@ public class ConfigurationPresentationFactory : IConfigurationPresentationFactor }; public MemberConfigurationResponseModel CreateMemberConfigurationResponseModel() => - new() - { - ReservedFieldNames = _reservedFieldNamesService.GetMemberReservedFieldNames(), - }; + new(); public MemberTypeConfigurationResponseModel CreateMemberTypeConfigurationResponseModel() => new() @@ -86,7 +65,6 @@ public class ConfigurationPresentationFactory : IConfigurationPresentationFactor { DisableDeleteWhenReferenced = _contentSettings.DisableDeleteWhenReferenced, DisableUnpublishWhenReferenced = _contentSettings.DisableUnpublishWhenReferenced, - ReservedFieldNames = _reservedFieldNamesService.GetMediaReservedFieldNames(), }; public MediaTypeConfigurationResponseModel CreateMediaTypeConfigurationResponseModel() => diff --git a/src/Umbraco.Cms.Api.Management/Factories/DataTypePresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DataTypePresentationFactory.cs index 944cc7c5cd..d5967b532b 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DataTypePresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DataTypePresentationFactory.cs @@ -1,8 +1,5 @@ - -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Management.ViewModels.DataType; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; @@ -34,21 +31,6 @@ public class DataTypePresentationFactory : IDataTypePresentationFactory _timeProvider = timeProvider; } - [Obsolete("Use constructor that takes a TimeProvider")] - public DataTypePresentationFactory( - IDataTypeContainerService dataTypeContainerService, - PropertyEditorCollection propertyEditorCollection, - IDataValueEditorFactory dataValueEditorFactory, - IConfigurationEditorJsonSerializer configurationEditorJsonSerializer) - : this( - dataTypeContainerService, - propertyEditorCollection, - dataValueEditorFactory, - configurationEditorJsonSerializer, - StaticServiceProvider.Instance.GetRequiredService()) - { - } - /// public async Task> CreateAsync(CreateDataTypeRequestModel requestModel) { diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentCollectionPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentCollectionPresentationFactory.cs index 228952b469..b63704cbb2 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentCollectionPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentCollectionPresentationFactory.cs @@ -1,5 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Api.Management.ViewModels.Document.Collection; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -9,11 +12,23 @@ namespace Umbraco.Cms.Api.Management.Factories; public class DocumentCollectionPresentationFactory : ContentCollectionPresentationFactory, IDocumentCollectionPresentationFactory { private readonly IPublicAccessService _publicAccessService; + private readonly IEntityService _entityService; + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")] public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService) - : base(mapper) + : this( + mapper, + publicAccessService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [ActivatorUtilitiesConstructor] + public DocumentCollectionPresentationFactory(IUmbracoMapper mapper, IPublicAccessService publicAccessService, IEntityService entityService) + : base(mapper) { _publicAccessService = publicAccessService; + _entityService = entityService; } protected override Task SetUnmappedProperties(ListViewPagedModel contentCollection, List collectionResponseModels) @@ -27,6 +42,8 @@ public class DocumentCollectionPresentationFactory : ContentCollectionPresentati } item.IsProtected = _publicAccessService.IsProtected(matchingContentItem).Success; + item.Ancestors = _entityService.GetPathKeys(matchingContentItem, omitSelf: true) + .Select(x => new ReferenceByIdModel(x)); } return Task.CompletedTask; diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentNotificationPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentNotificationPresentationFactory.cs index 7daf762b78..5a79c575a5 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentNotificationPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentNotificationPresentationFactory.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Api.Management.ViewModels.Document; +using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Core.Actions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; @@ -19,15 +19,14 @@ internal sealed class DocumentNotificationPresentationFactory : IDocumentNotific _backOfficeSecurityAccessor = backOfficeSecurityAccessor; } - public async Task> CreateNotificationModelsAsync(IContent content) + public Task> CreateNotificationModelsAsync(IContent content) { var subscribedActionIds = _notificationService - .GetUserNotifications(_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, content.Path)? - .Select(n => n.Action) - .ToArray() - ?? Array.Empty(); + .GetUserNotifications(_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, content.Path)? + .Select(n => n.Action) + .ToArray() ?? Array.Empty(); - return await Task.FromResult(_actionCollection + return Task.FromResult>(_actionCollection .Where(action => action.ShowInNotifier) .Select(action => new DocumentNotificationResponseModel { diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs index ba6ebfaf9a..9982b653b9 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs @@ -1,9 +1,6 @@ -using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Management.ViewModels.Document; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Api.Management.Factories; @@ -12,23 +9,8 @@ public class DocumentUrlFactory : IDocumentUrlFactory private readonly IPublishedUrlInfoProvider _publishedUrlInfoProvider; - [Obsolete("Use the constructor that takes all dependencies, scheduled for removal in v16")] - public DocumentUrlFactory(IDocumentUrlService documentUrlService) - : this(StaticServiceProvider.Instance.GetRequiredService()) - { - } - - [Obsolete("Use the constructor that takes all dependencies, scheduled for removal in v16")] - public DocumentUrlFactory(IDocumentUrlService documentUrlService, IPublishedUrlInfoProvider publishedUrlInfoProvider) - : this(publishedUrlInfoProvider) - { - - } - public DocumentUrlFactory(IPublishedUrlInfoProvider publishedUrlInfoProvider) - { - _publishedUrlInfoProvider = publishedUrlInfoProvider; - } + => _publishedUrlInfoProvider = publishedUrlInfoProvider; public async Task> CreateUrlsAsync(IContent content) { diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentVersionPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentVersionPresentationFactory.cs index 2c3b80fd3c..5006ccbaa7 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentVersionPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentVersionPresentationFactory.cs @@ -26,11 +26,19 @@ internal sealed class DocumentVersionPresentationFactory : IDocumentVersionPrese new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType) .Result), new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)), - new DateTimeOffset(contentVersion.VersionDate, TimeSpan.Zero), // todo align with datetime offset rework + new DateTimeOffset(contentVersion.VersionDate), contentVersion.CurrentPublishedVersion, contentVersion.CurrentDraftVersion, contentVersion.PreventCleanup); - public async Task> CreateMultipleAsync(IEnumerable contentVersions) => - await Task.WhenAll(contentVersions.Select(CreateAsync)); + public async Task> CreateMultipleAsync(IEnumerable contentVersions) + => await CreateMultipleImplAsync(contentVersions).ToArrayAsync(); + + private async IAsyncEnumerable CreateMultipleImplAsync(IEnumerable contentVersions) + { + foreach (ContentVersionMeta contentVersion in contentVersions) + { + yield return await CreateAsync(contentVersion); + } + } } diff --git a/src/Umbraco.Cms.Api.Management/Factories/IConfigurationPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IConfigurationPresentationFactory.cs index 3914a0e058..14870f0357 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IConfigurationPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IConfigurationPresentationFactory.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Api.Management.ViewModels.Document; +using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Api.Management.ViewModels.DocumentType; using Umbraco.Cms.Api.Management.ViewModels.Media; using Umbraco.Cms.Api.Management.ViewModels.MediaType; @@ -19,6 +19,7 @@ public interface IConfigurationPresentationFactory MemberTypeConfigurationResponseModel CreateMemberTypeConfigurationResponseModel() => throw new NotImplementedException(); + [Obsolete("No longer used. Scheduled for removal in Umbraco 18.")] MediaConfigurationResponseModel CreateMediaConfigurationResponseModel(); MediaTypeConfigurationResponseModel CreateMediaTypeConfigurationResponseModel() diff --git a/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs index a1e8a79edf..ac33b042eb 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Api.Management.ViewModels.User; +using Umbraco.Cms.Api.Management.ViewModels.User; using Umbraco.Cms.Api.Management.ViewModels.User.Current; using Umbraco.Cms.Api.Management.ViewModels.User.Item; using Umbraco.Cms.Core.Models; @@ -22,7 +22,7 @@ public interface IUserPresentationFactory Task CreateUserConfigurationModelAsync(); - Task CreateCurrentUserConfigurationModelAsync(); + Task CreateCurrentUserConfigurationModelAsync(); UserItemResponseModel CreateItemResponseModel(IUser user); diff --git a/src/Umbraco.Cms.Api.Management/Factories/RedirectUrlPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/RedirectUrlPresentationFactory.cs index 8a62d299eb..8cb09bf03a 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/RedirectUrlPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/RedirectUrlPresentationFactory.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Api.Management.ViewModels; +using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.RedirectUrlManagement; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Routing; @@ -22,6 +22,12 @@ public class RedirectUrlPresentationFactory : IRedirectUrlPresentationFactory var originalUrl = _publishedUrlProvider.GetUrlFromRoute(source.ContentId, source.Url, source.Culture); + // Even if the URL could not be extracted from the route, if we have a path as a the route for the original URL, we should display it. + if (originalUrl == "#" && source.Url.StartsWith('/')) + { + originalUrl = source.Url; + } + return new RedirectUrlResponseModel { OriginalUrl = originalUrl, diff --git a/src/Umbraco.Cms.Api.Management/Factories/RelationTypePresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/RelationTypePresentationFactory.cs index a6fb1cbae8..990312845c 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/RelationTypePresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/RelationTypePresentationFactory.cs @@ -40,7 +40,7 @@ public class RelationTypePresentationFactory : IRelationTypePresentationFactory _scopeProvider = scopeProvider; } - public async Task> CreateReferenceResponseModelsAsync( + public Task> CreateReferenceResponseModelsAsync( IEnumerable relationItemModels) { IReadOnlyCollection relationItemModelsCollection = relationItemModels.ToArray(); @@ -56,13 +56,16 @@ public class RelationTypePresentationFactory : IRelationTypePresentationFactory IReferenceResponseModel[] result = relationItemModelsCollection.Select(relationItemModel => relationItemModel.NodeType switch { - Constants.UdiEntityType.Document => MapDocumentReference(relationItemModel, slimEntities), - Constants.UdiEntityType.Media => _umbracoMapper.Map(relationItemModel), - Constants.UdiEntityType.Member => _umbracoMapper.Map(relationItemModel), + Constants.ReferenceType.Document => MapDocumentReference(relationItemModel, slimEntities), + Constants.ReferenceType.Media => _umbracoMapper.Map(relationItemModel), + Constants.ReferenceType.Member => _umbracoMapper.Map(relationItemModel), + Constants.ReferenceType.DocumentTypePropertyType => _umbracoMapper.Map(relationItemModel), + Constants.ReferenceType.MediaTypePropertyType => _umbracoMapper.Map(relationItemModel), + Constants.ReferenceType.MemberTypePropertyType => _umbracoMapper.Map(relationItemModel), _ => _umbracoMapper.Map(relationItemModel), }).WhereNotNull().ToArray(); - return await Task.FromResult(result); + return Task.FromResult>(result); } private IReferenceResponseModel? MapDocumentReference(RelationItemModel relationItemModel, diff --git a/src/Umbraco.Cms.Api.Management/Factories/TemporaryFileConfigurationPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/TemporaryFileConfigurationPresentationFactory.cs index c0aa1d1c28..2850304f1c 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/TemporaryFileConfigurationPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/TemporaryFileConfigurationPresentationFactory.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options; using Umbraco.Cms.Api.Management.ViewModels.TemporaryFile; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Media; @@ -22,8 +22,8 @@ public class TemporaryFileConfigurationPresentationFactory : ITemporaryFileConfi new() { ImageFileTypes = _imageUrlGenerator.SupportedImageFileTypes.ToArray(), - DisallowedUploadedFilesExtensions = _contentSettings.DisallowedUploadedFileExtensions, - AllowedUploadedFileExtensions = _contentSettings.AllowedUploadedFileExtensions, + DisallowedUploadedFilesExtensions = _contentSettings.DisallowedUploadedFileExtensions.ToArray(), + AllowedUploadedFileExtensions = _contentSettings.AllowedUploadedFileExtensions.ToArray(), MaxFileSize = _runtimeSettings.MaxRequestLength, }; } diff --git a/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs index 794cfa9bd9..099074aba5 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs @@ -1,10 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Api.Management.Mapping; using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.UserGroup; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Services; @@ -23,16 +21,6 @@ public class UserGroupPresentationFactory : IUserGroupPresentationFactory private readonly IPermissionPresentationFactory _permissionPresentationFactory; private readonly ILogger _logger; - [Obsolete("Use the new constructor instead, will be removed in v16.")] - public UserGroupPresentationFactory( - IEntityService entityService, - IShortStringHelper shortStringHelper, - ILanguageService languageService, - IPermissionPresentationFactory permissionPresentationFactory) - : this(entityService, shortStringHelper, languageService, permissionPresentationFactory, StaticServiceProvider.Instance.GetRequiredService>()) - { - } - public UserGroupPresentationFactory( IEntityService entityService, IShortStringHelper shortStringHelper, diff --git a/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs index e1ee38d51a..4d7ed3c853 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs @@ -1,13 +1,17 @@ -using Umbraco.Cms.Api.Management.Routing; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Umbraco.Cms.Api.Management.Routing; using Umbraco.Cms.Api.Management.Security; using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.User; using Umbraco.Cms.Api.Management.ViewModels.User.Current; -using Umbraco.Cms.Core; using Umbraco.Cms.Api.Management.ViewModels.User.Item; +using Umbraco.Cms.Api.Management.ViewModels.UserGroup; +using Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions; +using Umbraco.Cms.Core; 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.Mail; using Umbraco.Cms.Core.Media; @@ -20,7 +24,6 @@ namespace Umbraco.Cms.Api.Management.Factories; public class UserPresentationFactory : IUserPresentationFactory { - private readonly IEntityService _entityService; private readonly AppCaches _appCaches; private readonly MediaFileManager _mediaFileManager; @@ -31,7 +34,10 @@ public class UserPresentationFactory : IUserPresentationFactory private readonly IPasswordConfigurationPresentationFactory _passwordConfigurationPresentationFactory; private readonly IBackOfficeExternalLoginProviders _externalLoginProviders; private readonly SecuritySettings _securitySettings; + private readonly IUserService _userService; + private readonly IContentService _contentService; + [Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")] public UserPresentationFactory( IEntityService entityService, AppCaches appCaches, @@ -43,6 +49,35 @@ public class UserPresentationFactory : IUserPresentationFactory IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory, IOptionsSnapshot securitySettings, IBackOfficeExternalLoginProviders externalLoginProviders) + : this( + entityService, + appCaches, + mediaFileManager, + imageUrlGenerator, + userGroupPresentationFactory, + absoluteUrlBuilder, + emailSender, + passwordConfigurationPresentationFactory, + securitySettings, + externalLoginProviders, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public UserPresentationFactory( + IEntityService entityService, + AppCaches appCaches, + MediaFileManager mediaFileManager, + IImageUrlGenerator imageUrlGenerator, + IUserGroupPresentationFactory userGroupPresentationFactory, + IAbsoluteUrlBuilder absoluteUrlBuilder, + IEmailSender emailSender, + IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory, + IOptionsSnapshot securitySettings, + IBackOfficeExternalLoginProviders externalLoginProviders, + IUserService userService, + IContentService contentService) { _entityService = entityService; _appCaches = appCaches; @@ -54,6 +89,8 @@ public class UserPresentationFactory : IUserPresentationFactory _externalLoginProviders = externalLoginProviders; _securitySettings = securitySettings.Value; _absoluteUrlBuilder = absoluteUrlBuilder; + _userService = userService; + _contentService = contentService; } public UserResponseModel CreateResponseModel(IUser user) @@ -96,7 +133,7 @@ public class UserPresentationFactory : IUserPresentationFactory Kind = user.Kind }; - public async Task CreateCreationModelAsync(CreateUserRequestModel requestModel) + public Task CreateCreationModelAsync(CreateUserRequestModel requestModel) { var createModel = new UserCreateModel { @@ -108,10 +145,10 @@ public class UserPresentationFactory : IUserPresentationFactory Kind = requestModel.Kind }; - return await Task.FromResult(createModel); + return Task.FromResult(createModel); } - public async Task CreateInviteModelAsync(InviteUserRequestModel requestModel) + public Task CreateInviteModelAsync(InviteUserRequestModel requestModel) { var inviteModel = new UserInviteModel { @@ -122,10 +159,10 @@ public class UserPresentationFactory : IUserPresentationFactory Message = requestModel.Message, }; - return await Task.FromResult(inviteModel); + return Task.FromResult(inviteModel); } - public async Task CreateResendInviteModelAsync(ResendInviteUserRequestModel requestModel) + public Task CreateResendInviteModelAsync(ResendInviteUserRequestModel requestModel) { var inviteModel = new UserResendInviteModel { @@ -133,15 +170,14 @@ public class UserPresentationFactory : IUserPresentationFactory Message = requestModel.Message, }; - return await Task.FromResult(inviteModel); + return Task.FromResult(inviteModel); } - public async Task CreateCurrentUserConfigurationModelAsync() + public Task CreateCurrentUserConfigurationModelAsync() { - var model = new CurrenUserConfigurationResponseModel + var model = new CurrentUserConfigurationResponseModel { KeepUserLoggedIn = _securitySettings.KeepUserLoggedIn, - UsernameIsEmail = _securitySettings.UsernameIsEmail, PasswordConfiguration = _passwordConfigurationPresentationFactory.CreatePasswordConfigurationResponseModel(), // You should not be able to change any password or set 2fa if any providers has deny local login set. @@ -149,7 +185,7 @@ public class UserPresentationFactory : IUserPresentationFactory AllowTwoFactor = _externalLoginProviders.HasDenyLocalLogin() is false, }; - return await Task.FromResult(model); + return Task.FromResult(model); } public Task CreateUserConfigurationModelAsync() => @@ -165,7 +201,7 @@ public class UserPresentationFactory : IUserPresentationFactory AllowTwoFactor = _externalLoginProviders.HasDenyLocalLogin() is false, }); - public async Task CreateUpdateModelAsync(Guid existingUserKey, UpdateUserRequestModel updateModel) + public Task CreateUpdateModelAsync(Guid existingUserKey, UpdateUserRequestModel updateModel) { var model = new UserUpdateModel { @@ -182,7 +218,7 @@ public class UserPresentationFactory : IUserPresentationFactory model.UserGroupKeys = updateModel.UserGroupIds.Select(x => x.Id).ToHashSet(); - return await Task.FromResult(model); + return Task.FromResult(model); } public async Task CreateCurrentUserResponseModelAsync(IUser user) @@ -195,14 +231,14 @@ public class UserPresentationFactory : IUserPresentationFactory var contentStartNodeIds = user.CalculateContentStartNodeIds(_entityService, _appCaches); var documentStartNodeKeys = GetKeysFromIds(contentStartNodeIds, UmbracoObjectTypes.Document); - var permissions = presentationGroups.SelectMany(x => x.Permissions).ToHashSet(); + var permissions = GetAggregatedGranularPermissions(user, presentationGroups); var fallbackPermissions = presentationGroups.SelectMany(x => x.FallbackPermissions).ToHashSet(); var hasAccessToAllLanguages = presentationGroups.Any(x => x.HasAccessToAllLanguages); var allowedSections = presentationGroups.SelectMany(x => x.Sections).ToHashSet(); - return await Task.FromResult(new CurrentUserResponseModel() + return new CurrentUserResponseModel() { Id = presentationUser.Id, Email = presentationUser.Email, @@ -222,17 +258,83 @@ public class UserPresentationFactory : IUserPresentationFactory AllowedSections = allowedSections, IsAdmin = user.IsAdmin(), UserGroupIds = presentationUser.UserGroupIds, - }); + }; } - public async Task CreateCalculatedUserStartNodesResponseModelAsync(IUser user) + private HashSet GetAggregatedGranularPermissions(IUser user, IEnumerable presentationGroups) + { + var aggregatedPermissions = new HashSet(); + + var permissions = presentationGroups.SelectMany(x => x.Permissions).ToHashSet(); + + AggregateAndAddDocumentPermissions(user, aggregatedPermissions, permissions); + + AggregateAndAddDocumentPropertyValuePermissions(aggregatedPermissions, permissions); + + return aggregatedPermissions; + } + + private void AggregateAndAddDocumentPermissions(IUser user, HashSet aggregatedPermissions, HashSet permissions) + { + // The raw permission data consists of several permissions for each document. We want to aggregate this server-side so + // we return one set of aggregate permissions per document that the client will use. + + // Get the unique document keys that have granular permissions. + IEnumerable documentKeysWithGranularPermissions = permissions + .Where(x => x is DocumentPermissionPresentationModel) + .Cast() + .Select(x => x.Document.Id) + .Distinct(); + + foreach (Guid documentKey in documentKeysWithGranularPermissions) + { + // Retrieve the path of the document. + var path = _contentService.GetById(documentKey)?.Path; + if (string.IsNullOrEmpty(path)) + { + continue; + } + + // With the path we can call the same logic as used server-side for authorizing access to resources. + EntityPermissionSet permissionsForPath = _userService.GetPermissionsForPath(user, path); + aggregatedPermissions.Add(new DocumentPermissionPresentationModel + { + Document = new ReferenceByIdModel(documentKey), + Verbs = permissionsForPath.GetAllPermissions() + }); + } + } + + private static void AggregateAndAddDocumentPropertyValuePermissions(HashSet aggregatedPermissions, HashSet permissions) + { + // We also have permissions for document type/property type combinations. + // These don't have an ancestor relationship that we need to take into account, but should be aggregated + // and included in the set. + IEnumerable<((Guid DocumentTypeId, Guid PropertyTypeId) Key, ISet Verbs)> documentTypePropertyTypeKeysWithGranularPermissions = permissions + .Where(x => x is DocumentPropertyValuePermissionPresentationModel) + .Cast() + .GroupBy(x => (x.DocumentType.Id, x.PropertyType.Id)) + .Select(x => (x.Key, (ISet)x.SelectMany(y => y.Verbs).Distinct().ToHashSet())); + + foreach (((Guid DocumentTypeId, Guid PropertyTypeId) Key, ISet Verbs) documentTypePropertyTypeKey in documentTypePropertyTypeKeysWithGranularPermissions) + { + aggregatedPermissions.Add(new DocumentPropertyValuePermissionPresentationModel + { + DocumentType = new ReferenceByIdModel(documentTypePropertyTypeKey.Key.DocumentTypeId), + PropertyType = new ReferenceByIdModel(documentTypePropertyTypeKey.Key.PropertyTypeId), + Verbs = documentTypePropertyTypeKey.Verbs + }); + } + } + + public Task CreateCalculatedUserStartNodesResponseModelAsync(IUser user) { var mediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService, _appCaches); ISet mediaStartNodeKeys = GetKeysFromIds(mediaStartNodeIds, UmbracoObjectTypes.Media); var contentStartNodeIds = user.CalculateContentStartNodeIds(_entityService, _appCaches); ISet documentStartNodeKeys = GetKeysFromIds(contentStartNodeIds, UmbracoObjectTypes.Document); - return await Task.FromResult(new CalculatedUserStartNodesResponseModel() + return Task.FromResult(new CalculatedUserStartNodesResponseModel() { Id = user.Key, MediaStartNodeIds = mediaStartNodeKeys, diff --git a/src/Umbraco.Cms.Api.Management/Mapping/Document/DocumentMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/Document/DocumentMapDefinition.cs index dbc51a6f41..15eb6bafd8 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/Document/DocumentMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/Document/DocumentMapDefinition.cs @@ -67,7 +67,7 @@ public class DocumentMapDefinition : ContentMapDefinition DocumentPropertyValueGranularPermission.ContextType; + + public IGranularPermission MapFromDto(UserGroup2GranularPermissionDto dto) => + new DocumentPropertyValueGranularPermission() + { + Key = dto.UniqueId!.Value, + Permission = dto.Permission, + }; + + public Type PresentationModelToHandle => typeof(DocumentPropertyValuePermissionPresentationModel); + + public IEnumerable MapManyAsync(IEnumerable granularPermissions) + { + var intermediate = granularPermissions.Where(p => p.Key.HasValue).Select(p => + { + var parts = p.Permission.Split('|'); + return parts.Length == 2 && Guid.TryParse(parts[0], out Guid propertyTypeId) + ? new { DocumentTypeId = p.Key!.Value, PropertyTypeId = propertyTypeId, Verb = parts[1] } + : null; + }) + .WhereNotNull() + .ToArray(); + + var intermediateByDocumentType = intermediate.GroupBy(x => x.DocumentTypeId); + foreach (var documentTypeGroup in intermediateByDocumentType) + { + foreach (var propertyTypeGroup in documentTypeGroup.GroupBy(x => x.PropertyTypeId)) + { + yield return new DocumentPropertyValuePermissionPresentationModel + { + DocumentType = new ReferenceByIdModel(documentTypeGroup.Key), + PropertyType = new ReferenceByIdModel(propertyTypeGroup.Key), + Verbs = propertyTypeGroup + .Select(x => x.Verb) + .Where(verb => verb.IsNullOrWhiteSpace() is false) + .ToHashSet(), + }; + } + } + } + + public IEnumerable MapToGranularPermissions(IPermissionPresentationModel permissionViewModel) + { + if (permissionViewModel is not DocumentPropertyValuePermissionPresentationModel documentTypePermissionPresentationModel) + { + yield break; + } + + foreach (var verb in documentTypePermissionPresentationModel.Verbs.Distinct().DefaultIfEmpty(string.Empty)) + { + yield return new DocumentPropertyValueGranularPermission + { + Key = documentTypePermissionPresentationModel.DocumentType.Id, + Permission = $"{documentTypePermissionPresentationModel.PropertyType.Id}|{verb}" + }; + } + } +} diff --git a/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs index e9f2700f5a..c4efca3088 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/TrackedReferences/TrackedReferenceViewModelsMapDefinition.cs @@ -12,6 +12,9 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition mapper.Define((source, context) => new DocumentReferenceResponseModel(), Map); mapper.Define((source, context) => new MediaReferenceResponseModel(), Map); mapper.Define((source, context) => new MemberReferenceResponseModel(), Map); + mapper.Define((source, context) => new DocumentTypePropertyTypeReferenceResponseModel(), Map); + mapper.Define((source, context) => new MediaTypePropertyTypeReferenceResponseModel(), Map); + mapper.Define((source, context) => new MemberTypePropertyTypeReferenceResponseModel(), Map); mapper.Define((source, context) => new DefaultReferenceResponseModel(), Map); mapper.Define((source, context) => new ReferenceByIdModel(), Map); mapper.Define((source, context) => new ReferenceByIdModel(), Map); @@ -25,6 +28,7 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition target.Published = source.NodePublished; target.DocumentType = new TrackedReferenceDocumentType { + Id = source.ContentTypeKey, Alias = source.ContentTypeAlias, Icon = source.ContentTypeIcon, Name = source.ContentTypeName, @@ -38,6 +42,7 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition target.Name = source.NodeName; target.MediaType = new TrackedReferenceMediaType { + Id = source.ContentTypeKey, Alias = source.ContentTypeAlias, Icon = source.ContentTypeIcon, Name = source.ContentTypeName, @@ -51,6 +56,52 @@ public class TrackedReferenceViewModelsMapDefinition : IMapDefinition target.Name = source.NodeName; target.MemberType = new TrackedReferenceMemberType { + Id = source.ContentTypeKey, + Alias = source.ContentTypeAlias, + Icon = source.ContentTypeIcon, + Name = source.ContentTypeName, + }; + } + + // Umbraco.Code.MapAll + private void Map(RelationItemModel source, DocumentTypePropertyTypeReferenceResponseModel target, MapperContext context) + { + target.Id = source.NodeKey; + target.Name = source.NodeName; + target.Alias = source.NodeAlias; + target.DocumentType = new TrackedReferenceDocumentType + { + Id = source.ContentTypeKey, + Alias = source.ContentTypeAlias, + Icon = source.ContentTypeIcon, + Name = source.ContentTypeName, + }; + } + + // Umbraco.Code.MapAll + private void Map(RelationItemModel source, MediaTypePropertyTypeReferenceResponseModel target, MapperContext context) + { + target.Id = source.NodeKey; + target.Name = source.NodeName; + target.Alias = source.NodeAlias; + target.MediaType = new TrackedReferenceMediaType + { + Id = source.ContentTypeKey, + Alias = source.ContentTypeAlias, + Icon = source.ContentTypeIcon, + Name = source.ContentTypeName, + }; + } + + // Umbraco.Code.MapAll + private void Map(RelationItemModel source, MemberTypePropertyTypeReferenceResponseModel target, MapperContext context) + { + target.Id = source.NodeKey; + target.Name = source.NodeName; + target.Alias = source.NodeAlias; + target.MemberType = new TrackedReferenceMemberType + { + Id = source.ContentTypeKey, Alias = source.ContentTypeAlias, Icon = source.ContentTypeIcon, Name = source.ContentTypeName, diff --git a/src/Umbraco.Cms.Api.Management/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs b/src/Umbraco.Cms.Api.Management/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs index 7c6d109979..e4d3c236de 100644 --- a/src/Umbraco.Cms.Api.Management/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs +++ b/src/Umbraco.Cms.Api.Management/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs @@ -1,9 +1,8 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -22,19 +21,6 @@ public class BackOfficeAuthorizationInitializationMiddleware : IMiddleware private readonly IRuntimeState _runtimeState; private readonly WebRoutingSettings _webRoutingSettings; - [Obsolete("Use the non-obsolete constructor. This will be removed in Umbraco 16.")] - public BackOfficeAuthorizationInitializationMiddleware( - UmbracoRequestPaths umbracoRequestPaths, - IServiceProvider serviceProvider, - IRuntimeState runtimeState) - : this( - umbracoRequestPaths, - serviceProvider, - runtimeState, - StaticServiceProvider.Instance.GetRequiredService>()) - { - } - [Obsolete("Use the non-obsolete constructor. This will be removed in Umbraco 17.")] public BackOfficeAuthorizationInitializationMiddleware( UmbracoRequestPaths umbracoRequestPaths, diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 44a69ccc95..7e73c52f7d 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -816,6 +816,70 @@ ] } }, + "/umbraco/management/api/v1/data-type/{id}/referenced-by": { + "get": { + "tags": [ + "Data Type" + ], + "operationId": "GetDataTypeByIdReferencedBy", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PagedIReferenceResponseModel" + } + ] + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + }, + "403": { + "description": "The authenticated user does not have access to this resource" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, "/umbraco/management/api/v1/data-type/{id}/references": { "get": { "tags": [ @@ -872,6 +936,7 @@ "description": "The authenticated user does not have access to this resource" } }, + "deprecated": true, "security": [ { "Backoffice User": [ ] @@ -8902,6 +8967,17 @@ "nullable": true } } + }, + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PublishWithDescendantsResultModel" + } + ] + } + } } }, "400": { @@ -8982,6 +9058,89 @@ ] } }, + "/umbraco/management/api/v1/document/{id}/publish-with-descendants/result/{taskId}": { + "get": { + "tags": [ + "Document" + ], + "operationId": "GetDocumentByIdPublishWithDescendantsResultByTaskId", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "taskId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PublishWithDescendantsResultModel" + } + ] + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + }, + "403": { + "description": "The authenticated user does not have access to this resource" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, "/umbraco/management/api/v1/document/{id}/published": { "get": { "tags": [ @@ -9312,149 +9471,6 @@ ] } }, - "/umbraco/management/api/v1/document/{id}/validate": { - "put": { - "tags": [ - "Document" - ], - "operationId": "PutDocumentByIdValidate", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/UpdateDocumentRequestModel" - } - ] - } - }, - "text/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/UpdateDocumentRequestModel" - } - ] - } - }, - "application/*+json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/UpdateDocumentRequestModel" - } - ] - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "headers": { - "Umb-Notifications": { - "description": "The list of notifications produced during the request.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationHeaderModel" - }, - "nullable": true - } - } - } - }, - "400": { - "description": "Bad Request", - "headers": { - "Umb-Notifications": { - "description": "The list of notifications produced during the request.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationHeaderModel" - }, - "nullable": true - } - } - }, - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/ProblemDetails" - } - ] - } - } - } - }, - "404": { - "description": "Not Found", - "headers": { - "Umb-Notifications": { - "description": "The list of notifications produced during the request.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationHeaderModel" - }, - "nullable": true - } - } - }, - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/ProblemDetails" - } - ] - } - } - } - }, - "401": { - "description": "The resource is protected and requires an authentication token" - }, - "403": { - "description": "The authenticated user does not have access to this resource", - "headers": { - "Umb-Notifications": { - "description": "The list of notifications produced during the request.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationHeaderModel" - }, - "nullable": true - } - } - } - } - }, - "deprecated": true, - "security": [ - { - "Backoffice User": [ ] - } - ] - } - }, "/umbraco/management/api/v1.1/document/{id}/validate": { "put": { "tags": [ @@ -10611,6 +10627,61 @@ ] } }, + "/umbraco/management/api/v1/recycle-bin/document/referenced-by": { + "get": { + "tags": [ + "Document" + ], + "operationId": "GetRecycleBinDocumentReferencedBy", + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PagedIReferenceResponseModel" + } + ] + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + }, + "403": { + "description": "The authenticated user does not have access to this resource" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, "/umbraco/management/api/v1/recycle-bin/document/root": { "get": { "tags": [ @@ -16977,6 +17048,7 @@ "description": "The authenticated user does not have access to this resource" } }, + "deprecated": true, "security": [ { "Backoffice User": [ ] @@ -17762,6 +17834,61 @@ ] } }, + "/umbraco/management/api/v1/recycle-bin/media/referenced-by": { + "get": { + "tags": [ + "Media" + ], + "operationId": "GetRecycleBinMediaReferencedBy", + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/PagedIReferenceResponseModel" + } + ] + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + }, + "403": { + "description": "The authenticated user does not have access to this resource" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, "/umbraco/management/api/v1/recycle-bin/media/root": { "get": { "tags": [ @@ -23145,40 +23272,6 @@ ] } }, - "/umbraco/management/api/v1/published-cache/collect": { - "post": { - "tags": [ - "Published Cache" - ], - "operationId": "PostPublishedCacheCollect", - "responses": { - "501": { - "description": "Not Implemented", - "headers": { - "Umb-Notifications": { - "description": "The list of notifications produced during the request.", - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationHeaderModel" - }, - "nullable": true - } - } - } - }, - "401": { - "description": "The resource is protected and requires an authentication token" - } - }, - "deprecated": true, - "security": [ - { - "Backoffice User": [ ] - } - ] - } - }, "/umbraco/management/api/v1/published-cache/rebuild": { "post": { "tags": [ @@ -23277,28 +23370,6 @@ ] } }, - "/umbraco/management/api/v1/published-cache/status": { - "get": { - "tags": [ - "Published Cache" - ], - "operationId": "GetPublishedCacheStatus", - "responses": { - "501": { - "description": "Not Implemented" - }, - "401": { - "description": "The resource is protected and requires an authentication token" - } - }, - "deprecated": true, - "security": [ - { - "Backoffice User": [ ] - } - ] - } - }, "/umbraco/management/api/v1/redirect-management": { "get": { "tags": [ @@ -32334,7 +32405,7 @@ "schema": { "oneOf": [ { - "$ref": "#/components/schemas/CurrenUserConfigurationResponseModel" + "$ref": "#/components/schemas/CurrentUserConfigurationResponseModel" } ] } @@ -36136,6 +36207,9 @@ { "$ref": "#/components/schemas/DocumentPermissionPresentationModel" }, + { + "$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel" + }, { "$ref": "#/components/schemas/UnknownTypePermissionPresentationModel" } @@ -36279,23 +36353,18 @@ }, "additionalProperties": false }, - "CurrenUserConfigurationResponseModel": { + "CurrentUserConfigurationResponseModel": { "required": [ "allowChangePassword", "allowTwoFactor", "keepUserLoggedIn", - "passwordConfiguration", - "usernameIsEmail" + "passwordConfiguration" ], "type": "object", "properties": { "keepUserLoggedIn": { "type": "boolean" }, - "usernameIsEmail": { - "type": "boolean", - "deprecated": true - }, "passwordConfiguration": { "oneOf": [ { @@ -36424,6 +36493,9 @@ { "$ref": "#/components/schemas/DocumentPermissionPresentationModel" }, + { + "$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel" + }, { "$ref": "#/components/schemas/UnknownTypePermissionPresentationModel" } @@ -36912,7 +36984,6 @@ "type": "string" }, "translation": { - "minLength": 1, "type": "string" } }, @@ -37088,6 +37159,7 @@ }, "DocumentCollectionResponseModel": { "required": [ + "ancestors", "documentType", "id", "isProtected", @@ -37143,6 +37215,16 @@ "isProtected": { "type": "boolean" }, + "ancestors": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReferenceByIdModel" + } + ] + } + }, "updater": { "type": "string", "nullable": true @@ -37155,8 +37237,7 @@ "allowEditInvariantFromNonDefault", "allowNonExistingSegmentsCreation", "disableDeleteWhenReferenced", - "disableUnpublishWhenReferenced", - "reservedFieldNames" + "disableUnpublishWhenReferenced" ], "type": "object", "properties": { @@ -37171,14 +37252,6 @@ }, "allowNonExistingSegmentsCreation": { "type": "boolean" - }, - "reservedFieldNames": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string" - }, - "deprecated": true } }, "additionalProperties": false @@ -37289,6 +37362,48 @@ } } }, + "DocumentPropertyValuePermissionPresentationModel": { + "required": [ + "$type", + "documentType", + "propertyType", + "verbs" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "documentType": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReferenceByIdModel" + } + ] + }, + "propertyType": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReferenceByIdModel" + } + ] + }, + "verbs": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "DocumentPropertyValuePermissionPresentationModel": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel" + } + } + }, "DocumentRecycleBinItemResponseModel": { "required": [ "createDate", @@ -37456,6 +37571,7 @@ }, "DocumentTreeItemResponseModel": { "required": [ + "ancestors", "createDate", "documentType", "hasChildren", @@ -37495,6 +37611,16 @@ "isProtected": { "type": "boolean" }, + "ancestors": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReferenceByIdModel" + } + ] + } + }, "documentType": { "oneOf": [ { @@ -37740,6 +37866,45 @@ }, "additionalProperties": false }, + "DocumentTypePropertyTypeReferenceResponseModel": { + "required": [ + "$type", + "documentType", + "id" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "alias": { + "type": "string", + "nullable": true + }, + "documentType": { + "oneOf": [ + { + "$ref": "#/components/schemas/TrackedReferenceDocumentTypeModel" + } + ] + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "DocumentTypePropertyTypeReferenceResponseModel": "#/components/schemas/DocumentTypePropertyTypeReferenceResponseModel" + } + } + }, "DocumentTypePropertyTypeResponseModel": { "required": [ "alias", @@ -39373,8 +39538,7 @@ "MediaConfigurationResponseModel": { "required": [ "disableDeleteWhenReferenced", - "disableUnpublishWhenReferenced", - "reservedFieldNames" + "disableUnpublishWhenReferenced" ], "type": "object", "properties": { @@ -39383,14 +39547,6 @@ }, "disableUnpublishWhenReferenced": { "type": "boolean" - }, - "reservedFieldNames": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string" - }, - "deprecated": true } }, "additionalProperties": false @@ -39804,6 +39960,45 @@ }, "additionalProperties": false }, + "MediaTypePropertyTypeReferenceResponseModel": { + "required": [ + "$type", + "id", + "mediaType" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "alias": { + "type": "string", + "nullable": true + }, + "mediaType": { + "oneOf": [ + { + "$ref": "#/components/schemas/TrackedReferenceMediaTypeModel" + } + ] + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "MediaTypePropertyTypeReferenceResponseModel": "#/components/schemas/MediaTypePropertyTypeReferenceResponseModel" + } + } + }, "MediaTypePropertyTypeResponseModel": { "required": [ "alias", @@ -40218,20 +40413,7 @@ "additionalProperties": false }, "MemberConfigurationResponseModel": { - "required": [ - "reservedFieldNames" - ], "type": "object", - "properties": { - "reservedFieldNames": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string" - }, - "deprecated": true - } - }, "additionalProperties": false }, "MemberGroupItemResponseModel": { @@ -40582,6 +40764,45 @@ }, "additionalProperties": false }, + "MemberTypePropertyTypeReferenceResponseModel": { + "required": [ + "$type", + "id", + "memberType" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "alias": { + "type": "string", + "nullable": true + }, + "memberType": { + "oneOf": [ + { + "$ref": "#/components/schemas/TrackedReferenceMemberTypeModel" + } + ] + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "MemberTypePropertyTypeReferenceResponseModel": "#/components/schemas/MemberTypePropertyTypeReferenceResponseModel" + } + } + }, "MemberTypePropertyTypeResponseModel": { "required": [ "alias", @@ -41754,11 +41975,20 @@ { "$ref": "#/components/schemas/DocumentReferenceResponseModel" }, + { + "$ref": "#/components/schemas/DocumentTypePropertyTypeReferenceResponseModel" + }, { "$ref": "#/components/schemas/MediaReferenceResponseModel" }, + { + "$ref": "#/components/schemas/MediaTypePropertyTypeReferenceResponseModel" + }, { "$ref": "#/components/schemas/MemberReferenceResponseModel" + }, + { + "$ref": "#/components/schemas/MemberTypePropertyTypeReferenceResponseModel" } ] } @@ -43132,6 +43362,23 @@ }, "additionalProperties": false }, + "PublishWithDescendantsResultModel": { + "required": [ + "isComplete", + "taskId" + ], + "type": "object", + "properties": { + "taskId": { + "type": "string", + "format": "uuid" + }, + "isComplete": { + "type": "boolean" + } + }, + "additionalProperties": false + }, "PublishedDocumentResponseModel": { "required": [ "documentType", @@ -44366,7 +44613,7 @@ }, "maxFileSize": { "type": "integer", - "format": "int32", + "format": "int64", "nullable": true } }, @@ -44396,8 +44643,15 @@ "additionalProperties": false }, "TrackedReferenceDocumentTypeModel": { + "required": [ + "id" + ], "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, "icon": { "type": "string", "nullable": true @@ -44414,8 +44668,15 @@ "additionalProperties": false }, "TrackedReferenceMediaTypeModel": { + "required": [ + "id" + ], "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, "icon": { "type": "string", "nullable": true @@ -44432,8 +44693,15 @@ "additionalProperties": false }, "TrackedReferenceMemberTypeModel": { + "required": [ + "id" + ], "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, "icon": { "type": "string", "nullable": true @@ -45715,6 +45983,9 @@ { "$ref": "#/components/schemas/DocumentPermissionPresentationModel" }, + { + "$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel" + }, { "$ref": "#/components/schemas/UnknownTypePermissionPresentationModel" } @@ -46138,6 +46409,9 @@ { "$ref": "#/components/schemas/DocumentPermissionPresentationModel" }, + { + "$ref": "#/components/schemas/DocumentPropertyValuePermissionPresentationModel" + }, { "$ref": "#/components/schemas/UnknownTypePermissionPresentationModel" } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs index 907b91cdac..e2ff1e609a 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs @@ -38,7 +38,7 @@ public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOpera Type = ReferenceType.SecurityScheme, Id = ManagementApiConfiguration.ApiSecurityName } - }, new string[] { } + }, [] } } }; diff --git a/src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs b/src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs index e7f0a597a6..778dbf691f 100644 --- a/src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs +++ b/src/Umbraco.Cms.Api.Management/Routing/BackOfficeAreaRoutes.cs @@ -1,12 +1,8 @@ -using System.Text; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Options; using Umbraco.Cms.Api.Management.Controllers.Security; using Umbraco.Cms.Api.Management.ServerEvents; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Routing; using Umbraco.Extensions; @@ -14,53 +10,28 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Routing; /// -/// Creates routes for the back office area +/// Creates routes for the back office area. /// public sealed class BackOfficeAreaRoutes : IAreaRoutes { private readonly IRuntimeState _runtimeState; - private readonly string _umbracoPathSegment; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public BackOfficeAreaRoutes( - IOptions globalSettings, - IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState) - { - _runtimeState = runtimeState; - _umbracoPathSegment = globalSettings.Value.GetUmbracoMvcArea(hostingEnvironment); - } + public BackOfficeAreaRoutes(IRuntimeState runtimeState) + => _runtimeState = runtimeState; - [Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 15.")] - public BackOfficeAreaRoutes( - IOptions globalSettings, - IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - UmbracoApiControllerTypeCollection apiControllers) : this(globalSettings, hostingEnvironment, runtimeState) - { - - } /// public void CreateRoutes(IEndpointRouteBuilder endpoints) { - - - switch (_runtimeState.Level) + if (_runtimeState.Level is RuntimeLevel.Install or RuntimeLevel.Upgrade or RuntimeLevel.Run) { - case RuntimeLevel.Install: - case RuntimeLevel.Upgrade: - case RuntimeLevel.Run: - MapMinimalBackOffice(endpoints); - endpoints.MapHub(_umbracoPathSegment + Constants.Web.BackofficeSignalRHub); - endpoints.MapHub(_umbracoPathSegment + Constants.Web.ServerEventSignalRHub); - break; - case RuntimeLevel.BootFailed: - case RuntimeLevel.Unknown: - case RuntimeLevel.Boot: - break; + MapMinimalBackOffice(endpoints); + + endpoints.MapHub(Constants.System.UmbracoPathSegment + Constants.Web.BackofficeSignalRHub); + endpoints.MapHub(Constants.System.UmbracoPathSegment + Constants.Web.ServerEventSignalRHub); } } @@ -70,7 +41,7 @@ public sealed class BackOfficeAreaRoutes : IAreaRoutes private void MapMinimalBackOffice(IEndpointRouteBuilder endpoints) { endpoints.MapUmbracoRoute( - _umbracoPathSegment, + Constants.System.UmbracoPathSegment, null!, string.Empty, "Index", @@ -82,7 +53,7 @@ public sealed class BackOfficeAreaRoutes : IAreaRoutes endpoints.MapControllerRoute( "catch-all-sections-to-client", - new StringBuilder(_umbracoPathSegment).Append("/{**slug}").ToString(), + $"{Constants.System.UmbracoPathSegment}/{{**slug}}", new { Controller = ControllerExtensions.GetControllerName(), diff --git a/src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs b/src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs index 6f19010775..1327e1e144 100644 --- a/src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs +++ b/src/Umbraco.Cms.Api.Management/Routing/PreviewRoutes.cs @@ -1,13 +1,9 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Options; using Umbraco.Cms.Api.Management.Preview; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Routing; -using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Routing; @@ -17,36 +13,23 @@ namespace Umbraco.Cms.Api.Management.Routing; public sealed class PreviewRoutes : IAreaRoutes { private readonly IRuntimeState _runtimeState; - private readonly string _umbracoPathSegment; - public PreviewRoutes( - IOptions globalSettings, - IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState) - { - _runtimeState = runtimeState; - _umbracoPathSegment = globalSettings.Value.GetUmbracoMvcArea(hostingEnvironment); - } + public PreviewRoutes(IRuntimeState runtimeState) + => _runtimeState = runtimeState; public void CreateRoutes(IEndpointRouteBuilder endpoints) { - switch (_runtimeState.Level) + if (_runtimeState.Level is RuntimeLevel.Install or RuntimeLevel.Upgrade or RuntimeLevel.Run) { - case RuntimeLevel.Install: - case RuntimeLevel.Upgrade: - case RuntimeLevel.Run: - endpoints.MapHub(GetPreviewHubRoute()); - break; - case RuntimeLevel.BootFailed: - case RuntimeLevel.Unknown: - case RuntimeLevel.Boot: - break; + endpoints.MapHub(GetPreviewHubRoute()); } } /// - /// Returns the path to the signalR hub used for preview + /// Gets the path to the SignalR hub used for preview. /// - /// Path to signalR hub - public string GetPreviewHubRoute() => $"/{_umbracoPathSegment}/{nameof(PreviewHub)}"; + /// + /// The path to the SignalR hub used for preview. + /// + public string GetPreviewHubRoute() => $"/{Constants.System.UmbracoPathSegment}/{nameof(PreviewHub)}"; } diff --git a/src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs index e5c36cf77c..1695d0f1e1 100644 --- a/src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeSignInManager.cs @@ -2,12 +2,10 @@ using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; @@ -50,37 +48,6 @@ public class BackOfficeSignInManager : UmbracoSignInManager claimsFactory, - IOptions optionsAccessor, - IOptions globalSettings, - ILogger> logger, - IAuthenticationSchemeProvider schemes, - IUserConfirmation confirmation, - IEventAggregator eventAggregator, - IOptions securitySettings) - : this( - userManager, - contextAccessor, - externalLogins, - claimsFactory, - optionsAccessor, - globalSettings, - logger, - schemes, - confirmation, - eventAggregator, - securitySettings, - StaticServiceProvider.Instance.GetRequiredService>(), - StaticServiceProvider.Instance.GetRequiredService() - ) - { - } - protected override string AuthenticationType => _backOfficeAuthenticationTypeSettings.Value.AuthenticationType; protected override string ExternalAuthenticationType => _backOfficeAuthenticationTypeSettings.Value.ExternalAuthenticationType; diff --git a/src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs index c8fc4d6107..fd3dc4e232 100644 --- a/src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs +++ b/src/Umbraco.Cms.Api.Management/Security/ExternalSignInAutoLinkOptions.cs @@ -17,7 +17,7 @@ public class ExternalSignInAutoLinkOptions /// Initializes a new instance of the class. /// /// - /// If null, the default will be the 'editor' group + /// If null, the default will be no groups. /// /// public ExternalSignInAutoLinkOptions( @@ -26,7 +26,7 @@ public class ExternalSignInAutoLinkOptions string? defaultCulture = null, bool allowManualLinking = true) { - DefaultUserGroups = defaultUserGroups ?? new[] { SecurityConstants.EditorGroupAlias }; + DefaultUserGroups = defaultUserGroups ?? []; AutoLinkExternalAccount = autoLinkExternalAccount; AllowManualLinking = allowManualLinking; _defaultCulture = defaultCulture; diff --git a/src/Umbraco.Cms.Api.Management/Security/TwoFactorLoginViewOptions.cs b/src/Umbraco.Cms.Api.Management/Security/TwoFactorLoginViewOptions.cs deleted file mode 100644 index d045797f10..0000000000 --- a/src/Umbraco.Cms.Api.Management/Security/TwoFactorLoginViewOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Umbraco.Cms.Api.Management.Security; - -/// -/// Options used as named options for 2fa providers -/// -public class TwoFactorLoginViewOptions -{ - /// - /// Gets or sets the path of the view to show when setting up this 2fa provider - /// - [Obsolete("Register the view in the backoffice instead. This will be removed in version 15.")] - public string? SetupViewPath { get; set; } -} diff --git a/src/Umbraco.Cms.Api.Management/Serialization/ConfigureUmbracoBackofficeJsonOptions.cs b/src/Umbraco.Cms.Api.Management/Serialization/ConfigureUmbracoBackofficeJsonOptions.cs index 8f668b268d..d66d47cf16 100644 --- a/src/Umbraco.Cms.Api.Management/Serialization/ConfigureUmbracoBackofficeJsonOptions.cs +++ b/src/Umbraco.Cms.Api.Management/Serialization/ConfigureUmbracoBackofficeJsonOptions.cs @@ -36,5 +36,7 @@ public class ConfigureUmbracoBackofficeJsonOptions : IConfigureNamedOptions /// The returned entities may include entities that outside of the user start node scope, but are needed to /// for browsing to the actual user start nodes. These entities will be marked as "no access" entities. + /// + /// This method does not support pagination, because it must load all entities explicitly in order to calculate + /// the correct result, given that user start nodes can be descendants of root nodes. Consumers need to apply + /// pagination to the result if applicable. /// IEnumerable RootUserAccessEntities(UmbracoObjectTypes umbracoObjectType, int[] userStartNodeIds); + /// + /// Calculates the applicable child entities for a given object type for users without root access. + /// + /// The object type. + /// The calculated start node paths for the user. + /// The key of the parent. + /// The number of applicable children to skip. + /// The number of applicable children to take. + /// The ordering to apply when fetching and paginating the children. + /// The total number of applicable children available. + /// A list of child entities applicable for the user. + /// + /// The returned entities may include entities that outside of the user start node scope, but are needed to + /// for browsing to the actual user start nodes. These entities will be marked as "no access" entities. + /// + IEnumerable ChildUserAccessEntities( + UmbracoObjectTypes umbracoObjectType, + string[] userStartNodePaths, + Guid parentKey, + int skip, + int take, + Ordering ordering, + out long totalItems) + { + totalItems = 0; + return []; + } + /// /// Calculates the applicable child entities from a list of candidate child entities for users without root access. /// diff --git a/src/Umbraco.Cms.Api.Management/Services/Entities/UserStartNodeEntitiesService.cs b/src/Umbraco.Cms.Api.Management/Services/Entities/UserStartNodeEntitiesService.cs index 1a5572303b..c811d2c9c7 100644 --- a/src/Umbraco.Cms.Api.Management/Services/Entities/UserStartNodeEntitiesService.cs +++ b/src/Umbraco.Cms.Api.Management/Services/Entities/UserStartNodeEntitiesService.cs @@ -1,8 +1,12 @@ -using Umbraco.Cms.Core; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Api.Management.Models.Entities; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Persistence.Querying; +using Umbraco.Cms.Core.Scoping; using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Services.Entities; @@ -10,8 +14,24 @@ namespace Umbraco.Cms.Api.Management.Services.Entities; public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService { private readonly IEntityService _entityService; + private readonly ICoreScopeProvider _scopeProvider; + private readonly IIdKeyMap _idKeyMap; - public UserStartNodeEntitiesService(IEntityService entityService) => _entityService = entityService; + [Obsolete("Please use the non-obsolete constructor. Scheduled for removal in V17.")] + public UserStartNodeEntitiesService(IEntityService entityService) + : this( + entityService, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public UserStartNodeEntitiesService(IEntityService entityService, ICoreScopeProvider scopeProvider, IIdKeyMap idKeyMap) + { + _entityService = entityService; + _scopeProvider = scopeProvider; + _idKeyMap = idKeyMap; + } /// public IEnumerable RootUserAccessEntities(UmbracoObjectTypes umbracoObjectType, int[] userStartNodeIds) @@ -43,6 +63,54 @@ public class UserStartNodeEntitiesService : IUserStartNodeEntitiesService .ToArray(); } + public IEnumerable ChildUserAccessEntities(UmbracoObjectTypes umbracoObjectType, string[] userStartNodePaths, Guid parentKey, int skip, int take, Ordering ordering, out long totalItems) + { + Attempt parentIdAttempt = _idKeyMap.GetIdForKey(parentKey, umbracoObjectType); + if (parentIdAttempt.Success is false) + { + totalItems = 0; + return []; + } + + var parentId = parentIdAttempt.Result; + IEntitySlim? parent = _entityService.Get(parentId); + if (parent is null) + { + totalItems = 0; + return []; + } + + IEntitySlim[] children; + if (userStartNodePaths.Any(path => $"{parent.Path},".StartsWith($"{path},"))) + { + // the requested parent is one of the user start nodes (or a descendant of one), all children are by definition allowed + children = _entityService.GetPagedChildren(parentKey, umbracoObjectType, skip, take, out totalItems, ordering: ordering).ToArray(); + return ChildUserAccessEntities(children, userStartNodePaths); + } + + // if one or more of the user start nodes are descendants of the requested parent, find the "next child IDs" in those user start node paths + // - e.g. given the user start node path "-1,2,3,4,5", if the requested parent ID is 3, the "next child ID" is 4. + var userStartNodePathIds = userStartNodePaths.Select(path => path.Split(Constants.CharArrays.Comma).Select(int.Parse).ToArray()).ToArray(); + var allowedChildIds = userStartNodePathIds + .Where(ids => ids.Contains(parentId)) + // given the previous checks, the parent ID can never be the last in the user start node path, so this is safe + .Select(ids => ids[ids.IndexOf(parentId) + 1]) + .Distinct() + .ToArray(); + + totalItems = allowedChildIds.Length; + if (allowedChildIds.Length == 0) + { + // the requested parent is outside the scope of any user start nodes + return []; + } + + // even though we know the IDs of the allowed child entities to fetch, we still use a Query to yield correctly sorted children + IQuery query = _scopeProvider.CreateQuery().Where(x => allowedChildIds.Contains(x.Id)); + children = _entityService.GetPagedChildren(parentKey, umbracoObjectType, skip, take, out totalItems, query, ordering).ToArray(); + return ChildUserAccessEntities(children, userStartNodePaths); + } + /// public IEnumerable ChildUserAccessEntities(IEnumerable candidateChildren, string[] userStartNodePaths) // child entities for users without root access should include: diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Dictionary/DictionaryItemTranslationModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Dictionary/DictionaryItemTranslationModel.cs index e71760fcf9..2763fb2f79 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Dictionary/DictionaryItemTranslationModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Dictionary/DictionaryItemTranslationModel.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; namespace Umbraco.Cms.Api.Management.ViewModels.Dictionary; @@ -7,6 +7,5 @@ public class DictionaryItemTranslationModel [Required] public string IsoCode { get; set; } = string.Empty; - [Required] public string Translation { get; set; } = string.Empty; } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Document/Collection/DocumentCollectionResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Document/Collection/DocumentCollectionResponseModel.cs index a7c209ad2a..391714346a 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Document/Collection/DocumentCollectionResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Document/Collection/DocumentCollectionResponseModel.cs @@ -11,5 +11,7 @@ public class DocumentCollectionResponseModel : ContentCollectionResponseModelBas public bool IsProtected { get; set; } + public IEnumerable Ancestors { get; set; } = []; + public string? Updater { get; set; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentConfigurationResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentConfigurationResponseModel.cs index f6673bcd5b..3fb63dadda 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentConfigurationResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentConfigurationResponseModel.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Api.Management.ViewModels.Document; +namespace Umbraco.Cms.Api.Management.ViewModels.Document; public class DocumentConfigurationResponseModel { @@ -9,7 +9,4 @@ public class DocumentConfigurationResponseModel public required bool AllowEditInvariantFromNonDefault { get; set; } public required bool AllowNonExistingSegmentsCreation { get; set; } - - [Obsolete("Use DocumentTypeConfigurationResponseModel.ReservedFieldNames from the ConfigurationDocumentTypeController endpoint instead.")] - public required ISet ReservedFieldNames { get; set; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Document/PublishWithDescendantsResultModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Document/PublishWithDescendantsResultModel.cs new file mode 100644 index 0000000000..8bc88600b0 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Document/PublishWithDescendantsResultModel.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.Document; + +public class PublishWithDescendantsResultModel +{ + public Guid TaskId { get; set; } + + public bool IsComplete { get; set; } +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Media/MediaConfigurationResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Media/MediaConfigurationResponseModel.cs index ff723b658f..913f245075 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Media/MediaConfigurationResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Media/MediaConfigurationResponseModel.cs @@ -1,11 +1,8 @@ -namespace Umbraco.Cms.Api.Management.ViewModels.Media; +namespace Umbraco.Cms.Api.Management.ViewModels.Media; public class MediaConfigurationResponseModel { public required bool DisableDeleteWhenReferenced { get; set; } public required bool DisableUnpublishWhenReferenced { get; set; } - - [Obsolete("Use MediaTypeConfigurationResponseModel.ReservedFieldNames from the ConfigurationMediaTypeController endpoint instead.")] - public required ISet ReservedFieldNames { get; set; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Member/MemberConfigurationResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Member/MemberConfigurationResponseModel.cs index a3434583bd..701d1fbe69 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Member/MemberConfigurationResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Member/MemberConfigurationResponseModel.cs @@ -1,7 +1,6 @@ -namespace Umbraco.Cms.Api.Management.ViewModels.Member; +namespace Umbraco.Cms.Api.Management.ViewModels.Member; +[Obsolete("No longer used. Scheduled for removal in Umbraco 18.")] public class MemberConfigurationResponseModel { - [Obsolete("Use MemberTypeConfigurationResponseModel.ReservedFieldNames from the ConfigurationMemberTypeController endpoint instead.")] - public required ISet ReservedFieldNames { get; set; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Server/VersionResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Server/VersionResponseModel.cs deleted file mode 100644 index d421d99095..0000000000 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Server/VersionResponseModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Umbraco.Cms.Api.Management.ViewModels.Server; - -[Obsolete("Not used. Will be removed in V15.")] -public class VersionResponseModel -{ - [Required] - public string Version { get; set; } = string.Empty; -} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/TemporaryFile/TemporaryFileConfigurationResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/TemporaryFile/TemporaryFileConfigurationResponseModel.cs index 369f6fa756..d544b89286 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/TemporaryFile/TemporaryFileConfigurationResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/TemporaryFile/TemporaryFileConfigurationResponseModel.cs @@ -8,5 +8,5 @@ public class TemporaryFileConfigurationResponseModel public string[] AllowedUploadedFileExtensions { get; set; } = Array.Empty(); - public int? MaxFileSize { get; set; } + public long? MaxFileSize { get; set; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/ContentTypePropertyTypeReferenceResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/ContentTypePropertyTypeReferenceResponseModel.cs new file mode 100644 index 0000000000..83dc6a1a7a --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/ContentTypePropertyTypeReferenceResponseModel.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; + +public abstract class ContentTypePropertyTypeReferenceResponseModel : ReferenceResponseModel +{ + public string? Alias { get; set; } +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/DocumentTypePropertyTypeReferenceResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/DocumentTypePropertyTypeReferenceResponseModel.cs new file mode 100644 index 0000000000..b2ef3e4a0a --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/DocumentTypePropertyTypeReferenceResponseModel.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; + +public class DocumentTypePropertyTypeReferenceResponseModel : ContentTypePropertyTypeReferenceResponseModel +{ + public TrackedReferenceDocumentType DocumentType { get; set; } = new(); +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/MediaTypePropertyTypeReferenceResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/MediaTypePropertyTypeReferenceResponseModel.cs new file mode 100644 index 0000000000..1baf647654 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/MediaTypePropertyTypeReferenceResponseModel.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; + +public class MediaTypePropertyTypeReferenceResponseModel : ContentTypePropertyTypeReferenceResponseModel +{ + public TrackedReferenceMediaType MediaType { get; set; } = new(); +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/MemberTypePropertyTypeReferenceResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/MemberTypePropertyTypeReferenceResponseModel.cs new file mode 100644 index 0000000000..199a4b0ba1 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/MemberTypePropertyTypeReferenceResponseModel.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; + +public class MemberTypePropertyTypeReferenceResponseModel : ContentTypePropertyTypeReferenceResponseModel +{ + public TrackedReferenceMemberType MemberType { get; set; } = new(); +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/TrackedReferenceContentType.cs b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/TrackedReferenceContentType.cs index 31456abada..15ac365e41 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/TrackedReferenceContentType.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/TrackedReferences/TrackedReferenceContentType.cs @@ -1,7 +1,9 @@ -namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; +namespace Umbraco.Cms.Api.Management.ViewModels.TrackedReferences; public abstract class TrackedReferenceContentType { + public Guid Id { get; set; } + public string? Icon { get; set; } public string? Alias { get; set; } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Tree/DocumentTreeItemResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Tree/DocumentTreeItemResponseModel.cs index 094522b91a..1bde763102 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Tree/DocumentTreeItemResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Tree/DocumentTreeItemResponseModel.cs @@ -1,4 +1,3 @@ -using Umbraco.Cms.Api.Management.ViewModels.Content; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Api.Management.ViewModels.DocumentType; @@ -8,6 +7,8 @@ public class DocumentTreeItemResponseModel : ContentTreeItemResponseModel { public bool IsProtected { get; set; } + public IEnumerable Ancestors { get; set; } = []; + public DocumentTypeReferenceResponseModel DocumentType { get; set; } = new(); public IEnumerable Variants { get; set; } = Enumerable.Empty(); diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrenUserConfigurationResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrentUserConfigurationResponseModel.cs similarity index 54% rename from src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrenUserConfigurationResponseModel.cs rename to src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrentUserConfigurationResponseModel.cs index e8621949d3..d0a26d92b8 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrenUserConfigurationResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrentUserConfigurationResponseModel.cs @@ -2,14 +2,10 @@ using Umbraco.Cms.Api.Management.ViewModels.Security; namespace Umbraco.Cms.Api.Management.ViewModels.User.Current; -// TODO (V16): Correct the spelling on this class name, it should be CurrentUserConfigurationResponseModel. -public class CurrenUserConfigurationResponseModel +public class CurrentUserConfigurationResponseModel { public bool KeepUserLoggedIn { get; set; } - [Obsolete("Use the UserConfigurationResponseModel instead. This will be removed in V15.")] - public bool UsernameIsEmail { get; set; } - public required PasswordConfigurationResponseModel PasswordConfiguration { get; set; } public bool AllowChangePassword { get; set; } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPermissionViewModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPermissionPresentationModel.cs similarity index 99% rename from src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPermissionViewModel.cs rename to src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPermissionPresentationModel.cs index c925a5e71e..4f3083dbfb 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPermissionViewModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPermissionPresentationModel.cs @@ -3,5 +3,6 @@ namespace Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions; public class DocumentPermissionPresentationModel : IPermissionPresentationModel { public required ReferenceByIdModel Document { get; set; } + public required ISet Verbs { get; set; } } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPropertyValuePermissionPresentationModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPropertyValuePermissionPresentationModel.cs new file mode 100644 index 0000000000..dc12a56bd7 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/DocumentPropertyValuePermissionPresentationModel.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions; + +public class DocumentPropertyValuePermissionPresentationModel : IPermissionPresentationModel +{ + public required ReferenceByIdModel DocumentType { get; set; } + + public required ReferenceByIdModel PropertyType { get; set; } + + public required ISet Verbs { get; set; } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs index b6e2fa0b7b..13db115106 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Locking/SqliteEFCoreDistributedLockingMechanism.cs @@ -16,10 +16,10 @@ namespace Umbraco.Cms.Persistence.EFCore.Locking; internal sealed class SqliteEFCoreDistributedLockingMechanism : IDistributedLockingMechanism where T : DbContext { - private ConnectionStrings _connectionStrings; - private GlobalSettings _globalSettings; private readonly ILogger> _logger; private readonly Lazy> _efCoreScopeAccessor; + private GlobalSettings _globalSettings; + private ConnectionStrings _connectionStrings; public SqliteEFCoreDistributedLockingMechanism( ILogger> logger, @@ -31,29 +31,26 @@ internal sealed class SqliteEFCoreDistributedLockingMechanism : IDistributedL _efCoreScopeAccessor = efCoreScopeAccessor; _globalSettings = globalSettings.CurrentValue; _connectionStrings = connectionStrings.CurrentValue; - globalSettings.OnChange(x=>_globalSettings = x); - connectionStrings.OnChange(x=>_connectionStrings = x); + + globalSettings.OnChange(x => _globalSettings = x); + connectionStrings.OnChange(x => _connectionStrings = x); } public bool HasActiveRelatedScope => _efCoreScopeAccessor.Value.AmbientScope is not null; /// - public bool Enabled => _connectionStrings.IsConnectionStringConfigured() && - string.Equals(_connectionStrings.ProviderName, "Microsoft.Data.Sqlite", StringComparison.InvariantCultureIgnoreCase) && _efCoreScopeAccessor.Value.AmbientScope is not null; + public bool Enabled + => _connectionStrings.IsConnectionStringConfigured() && + string.Equals(_connectionStrings.ProviderName, Constants.ProviderNames.SQLLite, StringComparison.InvariantCultureIgnoreCase) && + _efCoreScopeAccessor.Value.AmbientScope is not null; // With journal_mode=wal we can always read a snapshot. public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null) - { - obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout; - return new SqliteDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value); - } + => new SqliteDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout ?? _globalSettings.DistributedLockingReadLockDefaultTimeout); // With journal_mode=wal only a single write transaction can exist at a time. public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null) - { - obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout; - return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value); - } + => new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout ?? _globalSettings.DistributedLockingWriteLockDefaultTimeout); private sealed class SqliteDistributedLock : IDistributedLock { @@ -104,9 +101,9 @@ internal sealed class SqliteEFCoreDistributedLockingMechanism : IDistributedL public DistributedLockType LockType { get; } - public void Dispose() => + public void Dispose() // Mostly no op, cleaned up by completing transaction in scope. - _parent._logger.LogDebug("Dropped {lockType} for id {id}", LockType, LockId); + => _parent._logger.LogDebug("Dropped {lockType} for id {id}", LockType, LockId); public override string ToString() => $"SqliteDistributedLock({LockId})"; @@ -115,15 +112,17 @@ internal sealed class SqliteEFCoreDistributedLockingMechanism : IDistributedL // Mostly no-op just check that we didn't end up ReadUncommitted for real. private void ObtainReadLock() { - IEfCoreScope? efCoreScope = _parent._efCoreScopeAccessor.Value.AmbientScope ?? throw new PanicException("No current ambient scope"); + IEfCoreScope? efCoreScope = _parent._efCoreScopeAccessor.Value.AmbientScope + ?? throw new PanicException("No current ambient scope"); - efCoreScope.ExecuteWithContextAsync(async database => + efCoreScope.ExecuteWithContextAsync(database => { if (database.Database.CurrentTransaction is null) { - throw new InvalidOperationException( - "SqliteDistributedLockingMechanism requires a transaction to function."); + throw new InvalidOperationException("SqliteDistributedLockingMechanism requires a transaction to function."); } + + return Task.CompletedTask; }); } @@ -131,14 +130,14 @@ internal sealed class SqliteEFCoreDistributedLockingMechanism : IDistributedL // lock occurs for entire database as opposed to row/table. private void ObtainWriteLock() { - IEfCoreScope? efCoreScope = _parent._efCoreScopeAccessor.Value.AmbientScope ?? throw new PanicException("No ambient scope"); + IEfCoreScope? efCoreScope = _parent._efCoreScopeAccessor.Value.AmbientScope + ?? throw new PanicException("No ambient scope"); efCoreScope.ExecuteWithContextAsync(async database => { if (database.Database.CurrentTransaction is null) { - throw new InvalidOperationException( - "SqliteDistributedLockingMechanism requires a transaction to function."); + throw new InvalidOperationException("SqliteDistributedLockingMechanism requires a transaction to function."); } var query = @$"UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id = {LockId.ToString(CultureInfo.InvariantCulture)}"; diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml index 90bf8d3598..8b4c1f328b 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml @@ -1,4 +1,3 @@ -@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.Extensions.Options; @using Umbraco.Cms.Api.Management.Controllers.Security @using Umbraco.Cms.Api.Management.Extensions @@ -11,10 +10,10 @@ @using Umbraco.Cms.Core.Serialization @using Umbraco.Cms.Web.Common.Hosting @using Umbraco.Extensions +@inject IOptions GlobalSettings @inject IOptions SecuritySettings @inject IEmailSender EmailSender @inject IHostingEnvironment HostingEnvironment -@inject IOptions GlobalSettings @inject IProfilerHtml ProfilerHtml @inject IBackOfficeExternalLoginProviders ExternalLogins @inject IBackOfficePathGenerator BackOfficePathGenerator @@ -22,7 +21,7 @@ @inject IJsonSerializer JsonSerializer @{ bool.TryParse(Context.Request.Query["umbDebug"], out var isDebug); - var backOfficePath = GlobalSettings.Value.GetBackOfficePath(HostingEnvironment); + var backOfficePath = HostingEnvironment.GetBackOfficePath(); var loginLogoImage = Url.RouteUrl(BackOfficeGraphicsController.LoginLogoRouteName, new {Version= "1"}); var loginLogoImageAlternative = Url.RouteUrl(BackOfficeGraphicsController.LoginLogoAlternativeRouteName, new {Version= "1"}); var loginBackgroundImage = Url.RouteUrl(BackOfficeGraphicsController.LoginBackGroundRouteName, new {Version= "1"}); @@ -37,7 +36,7 @@ - + diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoWebsite/Maintenance.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoWebsite/Maintenance.cshtml index 94de5f3c52..1d8ae6f01e 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoWebsite/Maintenance.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoWebsite/Maintenance.cshtml @@ -1,12 +1,8 @@ -@using Microsoft.Extensions.Options -@using Umbraco.Cms.Core.Configuration.Models @using Umbraco.Cms.Core.Hosting @using Umbraco.Cms.Core.Routing -@using Umbraco.Extensions -@inject IHostingEnvironment hostingEnvironment -@inject IOptions globalSettings +@inject IHostingEnvironment HostingEnvironment @{ - var backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment); + var backOfficePath = HostingEnvironment.GetBackOfficePath(); } @@ -17,7 +13,7 @@ Website is Under Maintainance - +