v11: Merge v10/feature/project-cleanup into v11/dev (#13112)
* New backoffice/add system text json configuration attribute (#12998) * Add SystemTextJsonConfigurationAttribute * Fix up formatting * Rename classes for clearer purpose Co-authored-by: Zeegaan <nge@umbraco.dk> * UmbracoPath should no longer be configurable (#13032) * UmbracoPath should no longer be configurable * Remove UmbracoPath configuration from all tests * Only contain style instead of full layout (#13033) * Only contain style instead of full layout (#13033) * Fix CodeQL duplicate "permissions" node and reformat * add an extra check to ensure the pips exist before adding a class to them * improve pip classList add/remove with no intermediary variable * Only contain style instead of full layout (#13033) * Ensure consistent margin on headings in tree root (#12992) * Ensure consistent margin on headings in tree root (#12992) (cherry picked from commit88bfef9e0d) * Bump version to 10.2.1 * Translate "User permissions for languages" feature to dutch (#12971) * Translate 'sectionsHelp' to Dutch * Translate 'selectLanguages' to Dutch * Transkate 'allowAccessToAllLanguages' to Dutch * Translate "User permissions for languages" feature to español (#12975) * Translate 'selectLanguages' to Español * Translate 'languagesHelp' to español * Translate 'allowAccessToAllLanguages' to spanish * Updated project references for Forms and Deploy in the JsonSchema project. (#13047) * Updated project references for Forms and Deploy in the JsonSchema project. (#13047) * UmbracoPath has been removed from the official schema store, remove temporary workaround from our schema generator as well (#13043) * add an extra check to ensure the pips exist before adding a class to them * improve pip classList add/remove with no intermediary variable * pass in parameters needed to member service (#13020) * Missing methods from IMemberService (#13022) * Add back methods to interface * Add default implementations to avoid breaking changes Co-authored-by: Zeegaan <nge@umbraco.dk> * New endpoint for web profiling dashboard (#13038) * Endpoint for web profiling dashboard * Add profiling API contract * New Backoffice: Published cache controller (#13034) * Add published cache controller (endpoints for the Published Status dashboard) * Update OpenAPI contract for published cache endpoints * Fix OpenApi spec Co-authored-by: Zeegaan <nge@umbraco.dk> * Bug fix for datepicker with offset time (#12847) * https://github.com/umbraco/Umbraco-CMS/issues/12844 * remove "X" from ng-attr Doing the test I killed the ng-if attr. But forgot it was there doing the commit Co-authored-by: Lucas Bach Bisgaard <lom@novicell.dk> * Make sure swagger tags and operations are sorted alphabetically (#13055) * Add spellcheck false to password inputs (#13059) * Add null check for variants in Grid Layout (#13060) This fixes a regression from 10.2.0 where the `variants` property was removed. * Add null check for variants in Grid Layout (#13060) This fixes a regression from 10.2.0 where the `variants` property was removed. * Fixes #12972 for validating legacy member passwords (#12973) * Fixes #12972 for validating legacy member passwords * Removed unused variable * removed unused variable * Fix issue toggling boolean between true/false after Save without refreshing * New backoffice: examine management controller (#12981) * Add ExamineManagementControllerBase * Add ExamineIndexModelFactory * Add IndexesExamineManagementController * Add proper attributes * Implement ExamineIndexViewModel.cs * formatting * Add comment about it working in .NET 7 * Add SearchersExamineManagementController.cs * Update comments about why it might throw errors * Add SearchResultViewModel * Add SearchExamineManagementController * Add ExamineSearcherValidationService * Rename ExamineSearcherValidationService.cs to ExamineSearcherFinderService.cs * Rename interface aswell * Add SearchExamineManagementController * Refactor ExamineSearcherFinderService * Add HasIndexRebuiltExamineManagementController.cs * Fix up formatting * Async actions * Add RebuildIndexExamineManagementController.cs * Rename IExamineIndexModelFactory to IExamineIndexViewModelFactory * Refactor HasIndexRebuilt endpoint to Index endpoint * Remove unused usings * Fix up DetailsExplanation * Create dedicated SearchersViewModel * Rename action * Rename RebuildIndex to Rebuild * Dont have changes in ExamineIndexModel * Make values strongly typed instead of generic lists of strings * Rename to non-plural * Rename to non-plural * Rename controller * Introduce ITemporaryIndexingService * Introduce ITemporaryIndexingService * Add SearcherName to view model * Move to new ExamineManagementControllerBase.cs * Refactor ExamineManagerService * Use init instead of setters * Make properties explicitly on models * Add DI * Apply suggestions from code review Co-authored-by: Kenn Jacobsen <kja@umbraco.dk> * Rename to IndexExamineManagementController * Return ViewModel instead of exception * Make view models non-nullable * Add examine management extension point * Rename to IndexingRebuilderService * Move rebuild logic to service * Fix up usages in IIndexingRebuilderService * Fix up DI * Fix OpenApi contract * Implement CanRebuild on IIndexingRebuilderService.cs Co-authored-by: Zeegaan <nge@umbraco.dk> Co-authored-by: Kenn Jacobsen <kja@umbraco.dk> * Update build script * Add BuildProjectReferences=false to dotnet pack * Internalize Umbraco.Cms.ManagementApi references * Make Searchers endpoint return ActionResult (#13068) * New backoffice - trees design (#12963) * Refactor: Add default versioned back office route attribute * Tree controller bases and first draft implementations for document, media and doctype * Move tree item view models to appropriate location * Fix missing parent * Refactor user entity access for testability * A bit of clean-up + handle user start nodes for items endpoint * Implement foldersOnly for folder tree * Items endpoint for document type tree * Strongly typed action results * Content + media recycle bin * Correct return type for swagger * Member type tree * Rename user start node handling to make a little more sense * Revert to faked admin start nodes in document tree * Media type tree * Data type tree * Relation type tree * Remove unused dependency from member type tree * Correct documentation for member type tree endpoint response types * Use icon constants * Add templates tree * Member group tree * Document blueprint tree * Partial views, scripts and stylesheets trees * Static files tree * Clarify "folders only" state * Comments and improved readability * Rename TreeControllerBase and TreeItemViewModel * Move recycle bin controller base to its own namespace * Moved tree base controllers to their own namespace * Common base class for tree view models * Remove ProblemDetails response type declaration from all actions * Add OpenApiTag * Various review comments * Dictionary item tree * Renamed all tree controllers to follow action/feature naming convention * Handle client culture state for document tree * Support "ignore user start nodes" for content and media + refactor how tree states work to make things more explicit * Fix or postpone a few TODOs * Make entity service able to paginate trashed children * Handle sorting explicitly * Re-apply VersionedApiBackOfficeRoute to install and upgrade controllers after merge * Use PagedViewModel instead of PagedResult for all trees * Explain the usage of UmbracoObjectTypes.Unknown * Introduce and apply GetMany pattern for dictionary items * Add a note about relation type caching * Fix broken test build + add unit tests for new localization service methods * Use new management API controller base * Entity repository should build document entities for document blueprints when getting paged entities (same as it does when getting specific entities) * Use Media type for Media recycle bin Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Move shared relation service to concrete implementations * Use inclusive language * Add 401 response type documentation to applicable trees * Refactor entity load for folder tree controller base + ensure that folders are only included in the first result page * Add (in-memory) pagination to dictionary tree * Make file system controller honor paging parameters * Support pagination in relation type tree * Clarify method name a bit for detecting tree root path requests * Update Open API schema to match new trees * Move from page number and page size to skip/take (with temporary workaround for lack of property skip/take pagination in current DB implementation) * Update OpenAPI schema to match skip/take * Update OpenAPI schema * Don't return paginated view models from "items" endpoints * Update OpenApi schema Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Use pattern matching to check if items is not `JArray` * Bump Smidge up to v4.1.1 * Removing X-XSS-Protection healthcheck * Redirect to a return URL if one is present on the querystring when logging into the back office * Fix issue 13023 - Cannot read properties of undefined (reading 'allowedActions') * Implement playwright acceptance tests instead of cypress (#13069) * fix up dependencies in package.json * Change configs to playwright * update types * remove cypress * add playwright * Fix up imports * Move up test files * Fix up scripts * Update user import * Remove allowEditInvariantFromNonDefault=True.spec.ts * Update docker container * Run playwright on pipelines * Install playwright * change urls * change to run playwright * Update reporter * create .env file when installing * update pipelines * Remove @in yml * Update Yaml script to use New-Item * Pipe object to Value * Update yaml to use "" not {} * Update localhost to proper port * Push package-lock.json * include dotenv in package.json * Add back umbraco.config.ts * Dont change launchSettings.json * Fix up pipelines * Change working directory * Add logging * Actually name the file * Remove .env from path * Add working directory * Add working directory to script * check env content * Update more working dir * Try making newline in YAML * add quotes * Try multiline infront of script * Move top statement * use https * Update to localhost 44331 * ignore https in config * Change linux to https * add timeouts for tests * Fix up url for linux * Update docker to use port 44331 -> 5000 * increase timeout * Update yaml * Remove Cypress references and fix URL/port mapping * Update umbraco-linux.docker * Generate ASP.NET HTTPS development certificate * Enable HTTPS development certificate in Docker * Dont run failing tests * Update HTTPS development certificate generation * Copy nuget.config to correct location in Docker file * do not run flaky test * update outputdir * Remove flaky tests * Update to dot reporter * Update to json-models-builders package * Check if results folder exists * Remove logging * Use bash to find folder * Dont use junit to report * only publish if folder exists * Add 5 retries for CI * search correct folder * Remove unused json file * Use line reporter * Remove umbraco.config.ts * Remove more flaky test * Add waits so we dont bombard SQLite * Add page as parameter * add page as parameter * Remove flaky macro test Co-authored-by: Zeegaan <nge@umbraco.dk> Co-authored-by: Ronald Barendse <ronald@barend.se> * Fix comment of view property in `ConfigurationFieldAttribute` (#13077) * Fix comment of view property in `ConfigurationFieldAttribute` * Update description of key property as well * Use char instead of string for DetectJson (#13019) * Use char instead of string for DetectJson * Use faster method * Change DetectIsJon method * Update acceptance test readme & scripts (#13078) * Update readme to reflect the change to playwright * Update test scripts * update README * update pipelines to run new script * update package.json scripts * dont include demo test in package.json * Add creation of blueprint test * Implement create test script Co-authored-by: Zeegaan <nge@umbraco.dk> * Refactor event handler away from keyup to ng-change * Don't use legacy icon for action delete * Added nullability attribute to IsNullOrWhitespace * Fixes umbraco/Umbraco-CMS.Accessibility.Issues #63 and #61 * Revert "Removing X-XSS-Protection healthcheck" (#13096) This reverts commit696475ebf2. Co-authored-by: Zeegaan <nge@umbraco.dk> * New backoffice: New Api controllers (#12983) * Create migrate Language controller to Umbraco.Cms:ManagementApi * Add proper language mapping * Update mapping to handle if language name is null * Uncomment code * Add new language view model * Add LanguageViewModelMapping * Add mapper registration * Fix up AddMappers extension method * Implement mapping IEnumerable of languages * Change action signatures to ViewModel instead of model * Seperate logic from controller into service * Move LanguageService.cs * Register service * Fix up mapping * Add null check to mapping instead of controller * Map to ILanguage instead of implementation * Fix up null check * Implement ProblemDetailsBuilder.cs * Rename duplicate method * Use builder in actions * Implement new Paged models * Create language controller base * Use pagedModel for GetAllLanguages * Create GetAllLanguagesPaged method * Split language controller into single APIs * Fix up controllers with API versions * Map Total property * Fix up route and naming for GetLanguageController.cs * Fix up naming for language controllers * New folder structure * Add culture controllers * Map CultureInfos to paged CultureViewModel * Remove wierd include in csproj * Refactor controller to return pagedViewModel instead of dictionary * Fix up mapping to map single and enumerable * Register mapping * Add apiversion to controller * Add inheritdoc * Create DictionaryControllerBase.cs * Add delete controller * Only use HttpDelete for deletes * Check also if language exists in service * Split Save action into Create & Update actions * Update Http attributes on Create and update controllers * Proper routing for delete controller * Add api version * Make action async * Implement CreateDictionaryController * Create DictionaryViewModel.cs * Use viewmodel instead of values * Create get by int DictionaryController * Add view models * Rename controller * Rename DictionaryViewModel to DictionaryItemViewModel * use created instead of ID * Apply DataContract/Datamember to view model * change to guid instead of Guid * Use proper responses instead of return models when creating/updating * Implement new IDictionaryService * Implement new MoveController * Use new service in mapper * Remove unused method * Add DictionaryViewModelMapDefinition * Create MoveOrCopyViewModel * Proper Http action * Create UpdateDictionary controller * Map IDictionaryItem to DictionaryViewModel * Add JsonPatching * Add UpdateDictionaryController.cs * Map DictionaryTranslationsDisplays properly * ParentId should be nullable * Add new DictionaryTranslationViewModel.cs * Remove translationViewModel * Add Id and Key to DictionaryTranslationDisplay.cs * Implement IDictionaryFactory.cs * Create DictionaryViewModels and do not use IEntity * Map to new view models instead of displays * Register the factiories in the service container * Remove newtonsoft * Add serializing to and from PatchDocs * Use JsonPatchViewModel instead of object type * Add JsonPatch.Net to csproj * Implement JsonPatchService.cs * Register JsonPatchService * Make model non-nullable * Update controllers to use new attributes * Rename MoveViewModel.cs * Remove NameIsDirty as that is legacy from how we used to handle updating * Add GetAllDictionaryController * Add todo to DictionaryControllerBase * Add ExportDictionaryController * Add ImportDictionaryController * Remove unused umbraco mapper * Add upload dictionary controller * Create Dictionary import view models * Update UploadDictionaryController with view models * Remove unused using * Implement pagedviewmodel for GetAllDictionaryController.cs * Add dictionary overview viewmodels * Add mapping for DictionaryOverViewModel * Update Dictionary controller to use new viewmodel * Fix up attributes for UploadDictionaryController * Make actions async * Make controller bases abstract * Fix after merge * New backoffice: Analytics controller (#12957) * Add AnalyticsControllerBase * Add AnalyticsViewModel * Add GetAnalyticsController * Update ViewModel to use System.Text.Json * Add SetAnalyticsController * Add AnalyticsLevelViewModel * Add GetAllAnalyticsController * Add viewmodel factory * Register factory for DI * Use factory for creation of ViewModel * Fix up AnalyticsLevelViewModel.cs * Use analyticsLevelViewModel * Add Enum validation to controller * Add OpenApi attributes * Add routing to GetAllAnalyticsController * Rename IPagedViewModelFactory * use new renamed PagedViewModelFactory * Make actions async * Make controller base abstract * Fix up after merge Co-authored-by: Zeegaan <nge@umbraco.dk> * New backoffice: Help controller (#12958) * Add HelpControllerBase * Add HelpPageViewModel * Add GetHelpController * Add viewmodel factory * Register factory for DI * Use PagedViewModelFactory for controller * Update baseurl to be nullable * Rename IPagedViewModelFactory * Use new renamed IPagedViewModelFactory * Dont use httpclient as field Co-authored-by: Zeegaan <nge@umbraco.dk> * New backoffice: Relation controller (#12961) * Add relation controller base * Add commen about auth * Add GetRelationController * Use mapping to viewmodel * Add RelationViewModel * Add RelationViewModelsMapDefinition.cs * Add viewmodel factory * Register factory for DI * Rename IPagedViewModelFactory * Add RelationViewModelFactory * Remove unused service * Add GetByChildRelationController * Add relationViewModelFactory to DI * Add MappingBuilderExtensions * Add relationViewModelsMapDefinition to DI * Use PagedViewModel for child relations * Add CreateMultiple * Update GetRelationController to use factory instead of direct mapping * Update GetByChildRelationController to use relationViewModelFactory * Fix up after merge Co-authored-by: Zeegaan <nge@umbraco.dk> * New backoffice: Tracked references controller (#12966) * Add TrackedReferencesControllerBase.cs * Add GetTrackedReferencesController * Add relation model * Add ITrackedReferencesRepository.cs * Rename relation to RelationModel * Add RelationMapDefinition.cs * Add TrackedReferencesSkipTakeRepository * Rename RelationModelMapDefinition * Add new services to DI * Rename RelationModel to RelationItemModel * Implement TrackedReferencesSkipTakeService * Formatting * Add RelationItemViewModel.cs * Add mapdefition * Update TrackedReferencesController to use new PagedViewModel * Add service to DI * imlement proper routing * Add async to GetTrackedReferencesController.cs * Rename action to get * Add DescendantTrackedReferencesController * make filterMustBeIsDependency nullable * Use count instead of capacity * Rename controller * Add MultipleTrackedReferencesController.cs * Refactor TrackedReferencesService to not return pagedViewModel * Remove TODO untill consensus on convention * Formatting * Delete old duplicate controller * Fix up naming * Fix up naming and fixed todo * Fix up mapdefinition registrations * Rename controllers * Fix naming * Fix nullable tree * Fix up relation controller & action names * Fix naming * Fix up multiple to not be post * Apply suggestions from code review Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Remove [ApiVersion] from each individual controller and added to base instead * Added missing semi-colon * Update all "non-async async" endpoints to return Task.FromResult * Fixed up LanguageViewModels namespace * Return proper status code * Update xml documentation * Rename GetAllCultureController * Change return type attribute to proper Model * Change return type attribute to correct model * Add clarifying comment * Change return type attribute to proper model * Rename ById to by key * Update DictionaryOverviewViewModel to use Key instead of ID * Implement factory method to create viewModel instead of using services in mapper * Fix up DictionaryItemViewModel to use Parent GUID instead of ID * Update return type attribute to correct model * Update key to actual GUID instead of string * Update route to not include "delete" * Remove redundant IActionResult specification * Update responseType attribute to correct models * Update OpenApiTag for DictionaryControllerBase * Update ResponseType attribute to correct models * update variable name to not be "XD" * Update ResponseType attribute to correct model * Update route to not include "update" as it is redundant * Update produces responsetype attributes to correct models * Use IJsonSerializer abstraction instead of JsonConvert directly * Remove unused field * Change ResponseType attribute to correct model * change TrackedReferencesSkipTakeService to return pagedModels directly * Remove duplicate DI * change to better variable names * Move RelationItemModel to core * Remove empty folder * Remove ITrackedReferencesSkipTakeService and add it to TrackedReferencesService * remove TrackedReferencesSkipTakeRepository and add to TrackedReferencesRepository.cs * Fix up TrackedReferencesService to not use SkipTake repository * Remove AddRepositories from ManagementApiComposer * Transition to ManagementApiBaseController * Fixes ResponseType attribute to correct model * Add loading files to service instead of having logic in controller * Add todo comment * Fix up routing for delete language * Use problem details builder * Add SystemTextJsonSerializer * Add SystemTextJsonSerializer * Remove unused usings * Remove obsolete MoveDictionaryController * Remove CreateDate and UpdateDate from DictionaryViewModel * Change response type to correct model * Remove PagedViewModelFactory.cs * Add obsolete message * Fix installer view models A setter is required for the modelbinder to be able to do its work * Update src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update ResponseType to correct Model * Update comment * Add FileUploadService * Add DictionaryFactory.cs to handle creation of viewmodel * Remove unused DI * Rename actions & controllers to reflect eachother * Update OpenApi.json * Add dictionary to openapi * Update in proper alphabetical order * Add trackedReferences to openapi * Fix open api doc Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Co-authored-by: Zeegaan <nge@umbraco.dk> * Backport project cleanup from #12907 * Remove empty Directory.Build.props * Fix GenerateAppsettingsSchema target * Re-add empty Directory.Build.props to prevent inheritance * Re-add missing JsonPatch.Net dependency * Fix merge issues (redundant TargetFramework property and appsettings-schema.json generation) * Fix and improve OpenAPI test assertion Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com> Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Co-authored-by: Zeegaan <nge@umbraco.dk> Co-authored-by: Kenn Jacobsen <kja@umbraco.dk> Co-authored-by: Mads Rasmussen <madsr@hey.com> Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Co-authored-by: Bjarne Fyrstenborg <bjarne_fyrstenborg@hotmail.com> Co-authored-by: Erik-Jan Westendorp <erikjanwestendorp@outlook.com> Co-authored-by: Andy Butland <abutland73@gmail.com> Co-authored-by: Sean <29239704+Bakersbakebread@users.noreply.github.com> Co-authored-by: Lucas Bach Bisgaard <rammi@rammi.dk> Co-authored-by: Lucas Bach Bisgaard <lom@novicell.dk> Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> Co-authored-by: Busra Sengul <aysebusrasengul@gmail.com> Co-authored-by: Justin Neville <justin@nevitech.co.uk> Co-authored-by: Jeavon Leopold <jeavon@crumpled-dog.com> Co-authored-by: Austin Gilbert <AGilbert@rbaconsulting.com> Co-authored-by: patrickdemooij9 <patrickdemooij98@hotmail.com> Co-authored-by: bakersbakebread <hello@seanthorne.co.uk> Co-authored-by: Karl Butler <kbutler@carbonsix.digital>
This commit is contained in:
38
.github/workflows/codeql-analysis.yml
vendored
38
.github/workflows/codeql-analysis.yml
vendored
@@ -2,29 +2,38 @@ name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['*/dev','*/contrib']
|
||||
branches:
|
||||
- '*/dev'
|
||||
- '*/contrib'
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ['*/dev','*/contrib']
|
||||
branches:
|
||||
- '*/dev'
|
||||
- '*/contrib'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
dotnetVersion: 7.x
|
||||
dotnetIncludePreviewVersions: true
|
||||
solution: umbraco.sln
|
||||
buildConfiguration: SkipTests
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_GENERATE_ASPNET_CERTIFICATE: false
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
|
||||
permissions:
|
||||
actions: read # for github/codeql-action/init to get workflow details
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/analyze to upload SARIF results
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
@@ -35,14 +44,17 @@ jobs:
|
||||
with:
|
||||
config-file: ./.github/config/codeql-config.yml
|
||||
|
||||
- name: Setup dotnet
|
||||
- name: Use .NET ${{ env.dotnetVersion }}
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: '7.x'
|
||||
include-prerelease: true
|
||||
dotnet-version: ${{ env.dotnetVersion }}
|
||||
include-prerelease: ${{ env.dotnetIncludePreviewVersions }}
|
||||
|
||||
- name: dotnet build
|
||||
run: dotnet build umbraco.sln -c SkipTests
|
||||
- name: Run dotnet restore
|
||||
run: dotnet restore ${{ env.solution }}
|
||||
|
||||
- name: Run dotnet build
|
||||
run: dotnet build ${{ env.solution }} --configuration ${{ env.buildConfiguration }} --no-restore -p:ContinuousIntegrationBuild=true
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Company>Umbraco HQ</Company>
|
||||
<Authors>Umbraco</Authors>
|
||||
<Copyright>Copyright © Umbraco $([System.DateTime]::Today.ToString('yyyy'))</Copyright>
|
||||
|
||||
@@ -25,6 +25,7 @@ parameters:
|
||||
variables:
|
||||
nodeVersion: 16.17.0
|
||||
dotnetVersion: 7.x
|
||||
dotnetIncludePreviewVersions: true
|
||||
solution: umbraco.sln
|
||||
buildConfiguration: Release
|
||||
UMBRACO__CMS__GLOBAL__ID: 00000000-0000-0000-0000-000000000042
|
||||
@@ -72,7 +73,7 @@ stages:
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet restore
|
||||
inputs:
|
||||
@@ -110,7 +111,7 @@ stages:
|
||||
}
|
||||
}
|
||||
|
||||
dotnet pack $(solution) --configuration $(buildConfiguration) --no-build --output $(Build.ArtifactStagingDirectory)/nupkg
|
||||
dotnet pack $(solution) --configuration $(buildConfiguration) --no-build -p:BuildProjectReferences=false --output $(Build.ArtifactStagingDirectory)/nupkg
|
||||
- script: |
|
||||
sha="$(Build.SourceVersion)"
|
||||
sha=${sha:0:7}
|
||||
@@ -246,7 +247,7 @@ stages:
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test
|
||||
inputs:
|
||||
@@ -283,7 +284,7 @@ stages:
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Run dotnet test
|
||||
inputs:
|
||||
@@ -293,7 +294,7 @@ stages:
|
||||
testRunTitle: Integration Tests SQLite - $(Agent.OS)
|
||||
env:
|
||||
Tests__Database__DatabaseType: 'Sqlite'
|
||||
Umbraco__Cms__global__MainDomLock: 'FileSystemMainDomLock'
|
||||
Umbraco__CMS__Global__MainDomLock: 'FileSystemMainDomLock'
|
||||
|
||||
# Integration Tests (SQL Server)
|
||||
- job:
|
||||
@@ -336,12 +337,11 @@ stages:
|
||||
env:
|
||||
Tests__Database__DatabaseType: $(testDb)
|
||||
Tests__Database__SQLServerMasterConnectionString: $(connectionString)
|
||||
Umbraco__Cms__global__MainDomLock: 'SqlMainDomLock'
|
||||
Umbraco__CMS__Global__MainDomLock: 'SqlMainDomLock'
|
||||
|
||||
- stage: E2E
|
||||
variables:
|
||||
npm_config_cache: $(Pipeline.Workspace)/.npm_e2e
|
||||
CYPRESS_CACHE_FOLDER: $(Pipeline.Workspace)/cypress_binaries
|
||||
displayName: E2E Tests
|
||||
dependsOn: Build
|
||||
jobs:
|
||||
@@ -352,9 +352,9 @@ stages:
|
||||
- name: Umbraco__CMS__Unattended__InstallUnattended # Windows only
|
||||
value: true
|
||||
- name: Umbraco__CMS__Unattended__UnattendedUserName # Windows only
|
||||
value: Cypress Test
|
||||
value: Playwright Test
|
||||
- name: Umbraco__CMS__Unattended__UnattendedUserEmail # Windows only
|
||||
value: cypress@umbraco.com
|
||||
value: playwright@umbraco.com
|
||||
- name: Umbraco__CMS__Unattended__UnattendedUserPassword # Windows only
|
||||
value: UmbracoAcceptance123!
|
||||
- name: Umbraco__CMS__Global__InstallMissingDatabase # Windows only
|
||||
@@ -362,11 +362,11 @@ stages:
|
||||
- name: UmbracoDatabaseServer # Windows only
|
||||
value: (LocalDB)\MSSQLLocalDB
|
||||
- name: UmbracoDatabaseName # Windows only
|
||||
value: Cypress
|
||||
value: Playwright
|
||||
- name: ConnectionStrings__umbracoDbDSN # Windows only
|
||||
value: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true;
|
||||
- name: CYPRESS_BASE_URL
|
||||
value: http://localhost:8080
|
||||
- name: PLAYWRIGHT_BASE_URL
|
||||
value: https://localhost:8443
|
||||
strategy:
|
||||
matrix:
|
||||
Linux:
|
||||
@@ -375,6 +375,7 @@ stages:
|
||||
dockerImageName: umbraco-linux
|
||||
Windows:
|
||||
vmImage: 'windows-latest'
|
||||
DOTNET_GENERATE_ASPNET_CERTIFICATE: true # Automatically generate HTTPS development certificate on Windows
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
@@ -395,17 +396,15 @@ stages:
|
||||
"npm_e2e" | "$(Agent.OS)"
|
||||
"npm_e2e"
|
||||
path: $(npm_config_cache)
|
||||
- task: Cache@2
|
||||
displayName: Cache cypress binaries
|
||||
inputs:
|
||||
key: '"cypress_binaries" | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json'
|
||||
path: $(CYPRESS_CACHE_FOLDER)
|
||||
- task: PowerShell@2
|
||||
displayName: Generate Cypress.env.json
|
||||
displayName: Generate .env
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >
|
||||
@{ username = "$(Umbraco__CMS__Unattended__UnattendedUserEmail)"; password = "$(Umbraco__CMS__Unattended__UnattendedUserPassword)" } | ConvertTo-Json | Set-Content -Path "tests/Umbraco.Tests.AcceptanceTest/cypress.env.json"#
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/
|
||||
script: |
|
||||
New-Item -Path "." -Name ".env" -ItemType "file" -Value "UMBRACO_USER_LOGIN=$(Umbraco__CMS__Unattended__UnattendedUserEmail)
|
||||
UMBRACO_USER_PASSWORD=$(Umbraco__CMS__Unattended__UnattendedUserPassword)
|
||||
URL=$(PLAYWRIGHT_BASE_URL)"
|
||||
- script: npm ci --no-fund --no-audit --prefer-offline
|
||||
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/
|
||||
displayName: Run npm ci
|
||||
@@ -420,7 +419,7 @@ stages:
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
performMultiLevelLookup: true
|
||||
includePreviewVersions: true
|
||||
includePreviewVersions: $(dotnetIncludePreviewVersions)
|
||||
# Linux containers smooth
|
||||
- task: PowerShell@2
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
||||
@@ -433,10 +432,13 @@ stages:
|
||||
docker build -t $(dockerImageName):$sha -f $(dockerfile) .
|
||||
mkdir -p $(Build.ArtifactStagingDirectory)/docker-images
|
||||
docker save -o $(Build.ArtifactStagingDirectory)/docker-images/$(dockerImageName).$sha.tar $(dockerImageName):$sha
|
||||
docker run --name $(dockerImageName) -dp 8080:5000 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) $(dockerImageName):$sha
|
||||
|
||||
# Manually generate HTTPS development certificate on Linux
|
||||
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p UmbracoAcceptance123!
|
||||
dotnet dev-certs https --trust
|
||||
|
||||
docker run --name $(dockerImageName) -dp 8080:5000 -dp 8443:5001 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) -e ASPNETCORE_Kestrel__Certificates__Default__Password="UmbracoAcceptance123!" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v ${HOME}/.aspnet/https:/https/ $(dockerImageName):$sha
|
||||
docker ps
|
||||
# Windows containers take forever.
|
||||
# --no-launch-profile stops ASPNETCORE_ENVIRONMENT=Development which breaks the users.ts tests (smtp config = invite user button)
|
||||
# Urls matching docker setup.
|
||||
- task: PowerShell@2
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
@@ -446,10 +448,10 @@ stages:
|
||||
targetType: inline
|
||||
script: |
|
||||
dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg
|
||||
dotnet new umbraco --name Cypress -o . --no-restore
|
||||
dotnet new umbraco --name Playwright --no-restore --output .
|
||||
dotnet restore --configfile ./nuget.config
|
||||
dotnet build --configuration $(buildConfiguration) --no-restore
|
||||
Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile --urls $(CYPRESS_BASE_URL)"
|
||||
Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile --urls $(PLAYWRIGHT_BASE_URL)"
|
||||
- task: PowerShell@2
|
||||
displayName: Wait for app
|
||||
inputs:
|
||||
@@ -457,31 +459,33 @@ stages:
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
script: |
|
||||
npm i -g wait-on
|
||||
wait-on -v --interval 1000 --timeout 120000 $(CYPRESS_BASE_URL)
|
||||
wait-on -v --interval 1000 --timeout 120000 $(PLAYWRIGHT_BASE_URL)
|
||||
- task: PowerShell@2
|
||||
displayName: Run Cypress (Desktop)
|
||||
displayName: Install Playwright
|
||||
inputs:
|
||||
targetType: inline
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
script: npx playwright install
|
||||
- task: PowerShell@2
|
||||
displayName: Run Playwright (Desktop)
|
||||
continueOnError: true
|
||||
inputs:
|
||||
targetType: inline
|
||||
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
|
||||
script: 'npm run test -- --reporter junit --reporter-options "mochaFile=results/test-output-D-[hash].xml,toConsole=true" --config="viewportHeight=1600,viewportWidth=2560,screenshotsFolder=cypress/artifacts/desktop/screenshots,videosFolder=cypress/artifacts/desktop/videos,videoUploadOnPasses=false"'
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish test results
|
||||
condition: always()
|
||||
inputs:
|
||||
testResultsFormat: 'JUnit'
|
||||
testResultsFiles: 'tests/Umbraco.Tests.AcceptanceTest/results/test-output-D-*.xml'
|
||||
mergeTestResults: true
|
||||
testRunTitle: "e2e - $(Agent.OS)"
|
||||
script: 'npm run test --ignore-certificate-errors'
|
||||
- bash: |
|
||||
if [ -f $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results/ ]; then
|
||||
echo "##vso[task.setVariable variable=myfileexists]true"
|
||||
fi
|
||||
- task: CopyFiles@2
|
||||
displayName: Prepare artifacts
|
||||
condition: always()
|
||||
condition: eq(variables.myfileexists, 'true')
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/cypress/artifacts
|
||||
targetFolder: $(Build.ArtifactStagingDirectory)/cypresss
|
||||
sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results/
|
||||
targetFolder: $(Build.ArtifactStagingDirectory)/playwright
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: "Publish test artifacts"
|
||||
condition: always()
|
||||
condition: eq(variables.myfileexists, 'true')
|
||||
inputs:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'E2E artifacts - $(Agent.OS) - Attempt #$(System.JobAttempt)'
|
||||
@@ -506,8 +510,8 @@ stages:
|
||||
inputs:
|
||||
artifact: nupkg
|
||||
path: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: dotnet restore
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet push
|
||||
inputs:
|
||||
command: restore
|
||||
projects: $(solution)
|
||||
@@ -533,8 +537,8 @@ stages:
|
||||
inputs:
|
||||
artifact: nupkg
|
||||
path: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: dotnet restore
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet push
|
||||
inputs:
|
||||
command: restore
|
||||
projects: $(solution)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EnablePackageValidation>false</EnablePackageValidation>
|
||||
@@ -13,7 +12,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<PackageReference Include="Umbraco.Deploy.Core" Version="10.0.0" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="10.0.0" />
|
||||
<PackageReference Include="Umbraco.Deploy.Core" Version="10.1.0" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="10.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NJsonSchema.Generation;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
|
||||
namespace JsonSchema
|
||||
{
|
||||
@@ -50,6 +49,9 @@ namespace JsonSchema
|
||||
{
|
||||
NJsonSchema.JsonSchema schema = _innerGenerator.Generate(typeof(AppSettings));
|
||||
|
||||
// TODO: when the "UmbracoPath" setter is removed from "GlobalSettings" (scheduled for V12), remove this line as well
|
||||
schema.Definitions["UmbracoCmsCoreConfigurationModelsGlobalSettings"]?.Properties?.Remove(nameof(GlobalSettings.UmbracoPath));
|
||||
|
||||
return JsonConvert.DeserializeObject<JObject>(schema.ToJson())!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<PropertyGroup>
|
||||
<Title>Umbraco CMS - Imaging - ImageSharp</Title>
|
||||
<Description>Adds imaging support using ImageSharp/ImageSharp.Web to Umbraco CMS.</Description>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<!-- TODO: Enable when final version is shipped (because there's currently no previous version) -->
|
||||
<EnablePackageValidation>false</EnablePackageValidation>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Builders;
|
||||
|
||||
public class ProblemDetailsBuilder
|
||||
{
|
||||
private string? _title;
|
||||
private string? _detail;
|
||||
private int _status = StatusCodes.Status400BadRequest;
|
||||
private string? _type;
|
||||
|
||||
public ProblemDetailsBuilder WithTitle(string title)
|
||||
{
|
||||
_title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProblemDetailsBuilder WithDetail(string detail)
|
||||
{
|
||||
_detail = detail;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProblemDetailsBuilder WithStatus(int status)
|
||||
{
|
||||
_status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProblemDetailsBuilder WithType(string type)
|
||||
{
|
||||
_type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProblemDetails Build() =>
|
||||
new()
|
||||
{
|
||||
Title = _title,
|
||||
Detail = _detail,
|
||||
Status = _status,
|
||||
Type = _type ?? "Error",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
public class AllAnalyticsController : AnalyticsControllerBase
|
||||
{
|
||||
[HttpGet("all")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<TelemetryLevel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<TelemetryLevel>> GetAll(int skip, int take)
|
||||
{
|
||||
TelemetryLevel[] levels = Enum.GetValues<TelemetryLevel>();
|
||||
return await Task.FromResult(new PagedViewModel<TelemetryLevel>
|
||||
{
|
||||
Total = levels.Length,
|
||||
Items = levels.Skip(skip).Take(take),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/analytics")]
|
||||
[OpenApiTag("Analytics")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class AnalyticsControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Analytics;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
public class GetAnalyticsController : AnalyticsControllerBase
|
||||
{
|
||||
private readonly IMetricsConsentService _metricsConsentService;
|
||||
|
||||
public GetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService;
|
||||
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(AnalyticsLevelViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<AnalyticsLevelViewModel> Get() => await Task.FromResult(new AnalyticsLevelViewModel { AnalyticsLevel = _metricsConsentService.GetConsentLevel() });
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Analytics;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Server;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Analytics;
|
||||
|
||||
public class SetAnalyticsController : AnalyticsControllerBase
|
||||
{
|
||||
private readonly IMetricsConsentService _metricsConsentService;
|
||||
|
||||
public SetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService;
|
||||
|
||||
[HttpPost]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> SetConsentLevel(AnalyticsLevelViewModel analyticsLevelViewModel)
|
||||
{
|
||||
if (!Enum.IsDefined(analyticsLevelViewModel.AnalyticsLevel))
|
||||
{
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Invalid AnalyticsLevel value",
|
||||
Detail = "The provided value for AnalyticsLevel is not valid",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
return BadRequest(invalidModelProblem);
|
||||
}
|
||||
|
||||
_metricsConsentService.SetConsentLevel(analyticsLevelViewModel.AnalyticsLevel);
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Culture;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Culture;
|
||||
|
||||
public class AllCultureController : CultureControllerBase
|
||||
{
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public AllCultureController(IUmbracoMapper umbracoMapper) => _umbracoMapper = umbracoMapper;
|
||||
|
||||
/// <summary>
|
||||
/// Returns all cultures available for creating languages.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<CultureViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<CultureViewModel>> GetAll(int skip, int take)
|
||||
{
|
||||
IEnumerable<CultureInfo> list = CultureInfo.GetCultures(CultureTypes.AllCultures)
|
||||
.DistinctBy(x => x.Name)
|
||||
.OrderBy(x => x.EnglishName)
|
||||
.Skip(skip)
|
||||
.Take(take);
|
||||
|
||||
return await Task.FromResult(_umbracoMapper.Map<PagedViewModel<CultureViewModel>>(list)!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Culture;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/culture")]
|
||||
[OpenApiTag("Culture")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class CultureControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree;
|
||||
|
||||
public class ChildrenDataTypeTreeController : DataTypeTreeControllerBase
|
||||
{
|
||||
public ChildrenDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
|
||||
: base(entityService, dataTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FolderTreeItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100, bool foldersOnly = false)
|
||||
{
|
||||
RenderFoldersOnly(foldersOnly);
|
||||
return await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DataType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DataType))]
|
||||
public class DataTypeTreeControllerBase : FolderTreeControllerBase<FolderTreeItemViewModel>
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public DataTypeTreeControllerBase(IEntityService entityService, IDataTypeService dataTypeService)
|
||||
: base(entityService) =>
|
||||
_dataTypeService = dataTypeService;
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.DataType;
|
||||
|
||||
protected override UmbracoObjectTypes FolderObjectType => UmbracoObjectTypes.DataTypeContainer;
|
||||
|
||||
protected override FolderTreeItemViewModel[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities)
|
||||
{
|
||||
var dataTypes = _dataTypeService
|
||||
.GetAll(entities.Select(entity => entity.Id).ToArray())
|
||||
.ToDictionary(contentType => contentType.Id);
|
||||
|
||||
return entities.Select(entity =>
|
||||
{
|
||||
FolderTreeItemViewModel viewModel = MapTreeItemViewModel(parentKey, entity);
|
||||
if (dataTypes.TryGetValue(entity.Id, out IDataType? dataType))
|
||||
{
|
||||
viewModel.Icon = dataType.Editor?.Icon ?? viewModel.Icon;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree;
|
||||
|
||||
public class ItemsDataTypeTreeController : DataTypeTreeControllerBase
|
||||
{
|
||||
public ItemsDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
|
||||
: base(entityService, dataTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
=> await GetItems(keys);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree;
|
||||
|
||||
public class RootDataTypeTreeController : DataTypeTreeControllerBase
|
||||
{
|
||||
public RootDataTypeTreeController(IEntityService entityService, IDataTypeService dataTypeService)
|
||||
: base(entityService, dataTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FolderTreeItemViewModel>>> Root(int skip = 0, int take = 100, bool foldersOnly = false)
|
||||
{
|
||||
RenderFoldersOnly(foldersOnly);
|
||||
return await GetRoot(skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Dictionary;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class AllDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public AllDictionaryController(ILocalizationService localizationService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list with all dictionary items
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IEnumerable{T}" />.
|
||||
/// </returns>
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<DictionaryOverviewViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<DictionaryOverviewViewModel>> All(int skip, int take)
|
||||
{
|
||||
IDictionaryItem[] items = _localizationService.GetDictionaryItemDescendants(null).ToArray();
|
||||
var list = new List<DictionaryOverviewViewModel>(items.Length);
|
||||
|
||||
// Build the proper tree structure, as we can have nested dictionary items
|
||||
BuildTree(list, items);
|
||||
|
||||
var model = new PagedViewModel<DictionaryOverviewViewModel>
|
||||
{
|
||||
Total = list.Count,
|
||||
Items = list.Skip(skip).Take(take),
|
||||
};
|
||||
return await Task.FromResult(model);
|
||||
}
|
||||
|
||||
// recursive method to build a tree structure from the flat structure returned above
|
||||
private void BuildTree(List<DictionaryOverviewViewModel> list, IDictionaryItem[] items, int level = 0, Guid? parentId = null)
|
||||
{
|
||||
IDictionaryItem[] children = items.Where(t => t.ParentId == parentId).ToArray();
|
||||
if (children.Any() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IDictionaryItem child in children.OrderBy(item => item.ItemKey))
|
||||
{
|
||||
DictionaryOverviewViewModel? display = _umbracoMapper.Map<IDictionaryItem, DictionaryOverviewViewModel>(child);
|
||||
if (display is not null)
|
||||
{
|
||||
display.Level = level;
|
||||
list.Add(display);
|
||||
}
|
||||
|
||||
BuildTree(list, items, level + 1, child.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Dictionary;
|
||||
using Umbraco.New.Cms.Core.Factories;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class ByIdDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IDictionaryFactory _dictionaryFactory;
|
||||
|
||||
public ByIdDictionaryController(
|
||||
ILocalizationService localizationService,
|
||||
IDictionaryFactory dictionaryFactory)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_dictionaryFactory = dictionaryFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary item by guid
|
||||
/// </summary>
|
||||
/// <param name="key">
|
||||
/// The id.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="DictionaryDisplay" />. Returns a not found response when dictionary item does not exist
|
||||
/// </returns>
|
||||
[HttpGet("{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DictionaryViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<DictionaryViewModel>> ByKey(Guid key)
|
||||
{
|
||||
IDictionaryItem? dictionary = _localizationService.GetDictionaryItemById(key);
|
||||
if (dictionary == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await Task.FromResult(_dictionaryFactory.CreateDictionaryViewModel(dictionary));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Dictionary;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class CreateDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private readonly ILogger<CreateDictionaryController> _logger;
|
||||
|
||||
public CreateDictionaryController(
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IOptionsSnapshot<GlobalSettings> globalSettings,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
ILogger<CreateDictionaryController> logger)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_localizedTextService = localizedTextService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new dictionary item
|
||||
/// </summary>
|
||||
/// <param name="dictionaryViewModel">The viewmodel to pass to the action</param>
|
||||
/// <returns>
|
||||
/// The <see cref="HttpResponseMessage" />.
|
||||
/// </returns>
|
||||
[HttpPost("create")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(CreatedResult), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
public async Task<ActionResult<int>> Create(DictionaryItemViewModel dictionaryViewModel)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dictionaryViewModel.Key.ToString()))
|
||||
{
|
||||
return ValidationProblem("Key can not be empty."); // TODO: translate
|
||||
}
|
||||
|
||||
if (_localizationService.DictionaryItemExists(dictionaryViewModel.Key.ToString()))
|
||||
{
|
||||
var message = _localizedTextService.Localize(
|
||||
"dictionaryItem",
|
||||
"changeKeyError",
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.GetUserCulture(_localizedTextService, _globalSettings),
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "0", dictionaryViewModel.Key.ToString() },
|
||||
});
|
||||
return await Task.FromResult(ValidationProblem(message));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Guid? parentGuid = null;
|
||||
|
||||
if (dictionaryViewModel.ParentId.HasValue)
|
||||
{
|
||||
parentGuid = dictionaryViewModel.ParentId;
|
||||
}
|
||||
|
||||
IDictionaryItem item = _localizationService.CreateDictionaryItemWithIdentity(
|
||||
dictionaryViewModel.Key.ToString(),
|
||||
parentGuid,
|
||||
string.Empty);
|
||||
|
||||
|
||||
return await Task.FromResult(Created($"api/v1.0/dictionary/{item.Key}", item.Key));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error creating dictionary with {Name} under {ParentId}", dictionaryViewModel.Key, dictionaryViewModel.ParentId);
|
||||
return await Task.FromResult(ValidationProblem("Error creating dictionary item"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class DeleteDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public DeleteDictionaryController(ILocalizationService localizationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
/// <summary>
|
||||
/// Deletes a data type with a given ID
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the dictionary item to delete</param>
|
||||
/// <returns>
|
||||
/// <see cref="HttpResponseMessage" />
|
||||
/// </returns>
|
||||
[HttpDelete("{key}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Delete(Guid key)
|
||||
{
|
||||
IDictionaryItem? foundDictionary = _localizationService.GetDictionaryItemByKey(key.ToString());
|
||||
|
||||
if (foundDictionary == null)
|
||||
{
|
||||
return await Task.FromResult(NotFound());
|
||||
}
|
||||
|
||||
IEnumerable<IDictionaryItem> foundDictionaryDescendants =
|
||||
_localizationService.GetDictionaryItemDescendants(foundDictionary.Key);
|
||||
|
||||
foreach (IDictionaryItem dictionaryItem in foundDictionaryDescendants)
|
||||
{
|
||||
_localizationService.Delete(dictionaryItem, _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1);
|
||||
}
|
||||
|
||||
_localizationService.Delete(foundDictionary, _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1);
|
||||
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/dictionary")]
|
||||
[OpenApiTag("Dictionary")]
|
||||
[ApiVersion("1.0")]
|
||||
// TODO: Add authentication
|
||||
public abstract class DictionaryControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class ExportDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IEntityXmlSerializer _entityXmlSerializer;
|
||||
|
||||
public ExportDictionaryController(ILocalizationService localizationService, IEntityXmlSerializer entityXmlSerializer)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_entityXmlSerializer = entityXmlSerializer;
|
||||
}
|
||||
|
||||
[HttpGet("export/{key:guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundObjectResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ExportDictionary(Guid key, bool includeChildren = false)
|
||||
{
|
||||
IDictionaryItem? dictionaryItem = _localizationService.GetDictionaryItemById(key);
|
||||
if (dictionaryItem is null)
|
||||
{
|
||||
return await Task.FromResult(NotFound("No dictionary item found with id "));
|
||||
}
|
||||
|
||||
XElement xml = _entityXmlSerializer.Serialize(dictionaryItem, includeChildren);
|
||||
|
||||
var fileName = $"{dictionaryItem.ItemKey}.udt";
|
||||
|
||||
// Set custom header so umbRequestHelper.downloadFile can save the correct filename
|
||||
HttpContext.Response.Headers.Add("x-filename", fileName);
|
||||
|
||||
return await Task.FromResult(File(Encoding.UTF8.GetBytes(xml.ToDataString()), MediaTypeNames.Application.Octet, fileName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class ImportDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IDictionaryService _dictionaryService;
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
private readonly ILoadDictionaryItemService _loadDictionaryItemService;
|
||||
|
||||
public ImportDictionaryController(
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IDictionaryService dictionaryService,
|
||||
IWebHostEnvironment webHostEnvironment,
|
||||
ILoadDictionaryItemService loadDictionaryItemService)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_dictionaryService = dictionaryService;
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_loadDictionaryItemService = loadDictionaryItemService;
|
||||
}
|
||||
|
||||
[HttpPost("import")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> ImportDictionary(string file, int? parentId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var filePath = Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data), file);
|
||||
if (_webHostEnvironment.ContentRootFileProvider.GetFileInfo(filePath) is null)
|
||||
{
|
||||
return await Task.FromResult(NotFound());
|
||||
}
|
||||
|
||||
IDictionaryItem dictionaryItem = _loadDictionaryItemService.Load(filePath, parentId);
|
||||
|
||||
return await Task.FromResult(Content(_dictionaryService.CalculatePath(dictionaryItem.ParentId, dictionaryItem.Id), MediaTypeNames.Text.Plain, Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Json.Patch;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Serialization;
|
||||
using Umbraco.Cms.ManagementApi.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Dictionary;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.JsonPatch;
|
||||
using Umbraco.New.Cms.Core.Factories;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class UpdateDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly IDictionaryService _dictionaryService;
|
||||
private readonly IDictionaryFactory _dictionaryFactory;
|
||||
private readonly IJsonPatchService _jsonPatchService;
|
||||
private readonly ISystemTextJsonSerializer _systemTextJsonSerializer;
|
||||
|
||||
public UpdateDictionaryController(
|
||||
ILocalizationService localizationService,
|
||||
IUmbracoMapper umbracoMapper,
|
||||
IDictionaryService dictionaryService,
|
||||
IDictionaryFactory dictionaryFactory,
|
||||
IJsonPatchService jsonPatchService,
|
||||
ISystemTextJsonSerializer systemTextJsonSerializer)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_dictionaryService = dictionaryService;
|
||||
_dictionaryFactory = dictionaryFactory;
|
||||
_jsonPatchService = jsonPatchService;
|
||||
_systemTextJsonSerializer = systemTextJsonSerializer;
|
||||
}
|
||||
|
||||
[HttpPatch("{id:Guid}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Update(Guid id, JsonPatchViewModel[] updateViewModel)
|
||||
{
|
||||
IDictionaryItem? dictionaryItem = _localizationService.GetDictionaryItemById(id);
|
||||
|
||||
if (dictionaryItem is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
DictionaryViewModel dictionaryToPatch = _umbracoMapper.Map<DictionaryViewModel>(dictionaryItem)!;
|
||||
|
||||
PatchResult? result = _jsonPatchService.Patch(updateViewModel, dictionaryToPatch);
|
||||
|
||||
if (result?.Result is null)
|
||||
{
|
||||
throw new JsonException("Could not patch the JsonPatchViewModel");
|
||||
}
|
||||
|
||||
DictionaryViewModel? updatedDictionaryItem = _systemTextJsonSerializer.Deserialize<DictionaryViewModel>(result.Result.ToJsonString());
|
||||
if (updatedDictionaryItem is null)
|
||||
{
|
||||
throw new JsonException("Could not serialize from PatchResult to DictionaryViewModel");
|
||||
}
|
||||
|
||||
IDictionaryItem dictionaryToSave = _dictionaryFactory.CreateDictionaryItem(updatedDictionaryItem!);
|
||||
_localizationService.Save(dictionaryToSave);
|
||||
return await Task.FromResult(Content(_dictionaryService.CalculatePath(dictionaryToSave.ParentId, dictionaryToSave.Id), MediaTypeNames.Text.Plain, Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Xml;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Models;
|
||||
using Umbraco.Cms.ManagementApi.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Dictionary;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.New.Cms.Core.Factories;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary;
|
||||
|
||||
public class UploadDictionaryController : DictionaryControllerBase
|
||||
{
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IUploadFileService _uploadFileService;
|
||||
private readonly IDictionaryFactory _dictionaryFactory;
|
||||
|
||||
public UploadDictionaryController(ILocalizedTextService localizedTextService, IUploadFileService uploadFileService, IDictionaryFactory dictionaryFactory)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_uploadFileService = uploadFileService;
|
||||
_dictionaryFactory = dictionaryFactory;
|
||||
}
|
||||
|
||||
[HttpPost("upload")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(DictionaryImportViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
public async Task<ActionResult<DictionaryImportViewModel>> Upload(IFormFile file)
|
||||
{
|
||||
FormFileUploadResult formFileUploadResult = _uploadFileService.TryLoad(file);
|
||||
if (formFileUploadResult.CouldLoad is false || formFileUploadResult.XmlDocument is null)
|
||||
{
|
||||
return await Task.FromResult(ValidationProblem(
|
||||
_localizedTextService.Localize("media", "failedFileUpload"),
|
||||
formFileUploadResult.ErrorMessage));
|
||||
}
|
||||
|
||||
DictionaryImportViewModel model = _dictionaryFactory.CreateDictionaryImportViewModel(formFileUploadResult);
|
||||
|
||||
if (!model.DictionaryItems.Any())
|
||||
{
|
||||
return ValidationProblem(
|
||||
_localizedTextService.Localize("media", "failedFileUpload"),
|
||||
_localizedTextService.Localize("dictionary", "noItemsInFile"));
|
||||
}
|
||||
|
||||
return await Task.FromResult(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Paging;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
public class ChildrenDictionaryItemTreeController : DictionaryItemTreeControllerBase
|
||||
{
|
||||
public ChildrenDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService, localizationService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<EntityTreeItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100)
|
||||
{
|
||||
if (PaginationService.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize, out ProblemDetails? error) == false)
|
||||
{
|
||||
return BadRequest(error);
|
||||
}
|
||||
|
||||
IDictionaryItem[] dictionaryItems = PaginatedDictionaryItems(
|
||||
pageNumber,
|
||||
pageSize,
|
||||
LocalizationService.GetDictionaryItemChildren(parentKey),
|
||||
out var totalItems);
|
||||
|
||||
EntityTreeItemViewModel[] viewModels = MapTreeItemViewModels(null, dictionaryItems);
|
||||
|
||||
PagedViewModel<EntityTreeItemViewModel> result = PagedViewModel(viewModels, totalItems);
|
||||
return await Task.FromResult(Ok(result));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DictionaryItem}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DictionaryItem))]
|
||||
// NOTE: at the moment dictionary items aren't supported by EntityService, so we have little use of the
|
||||
// tree controller base. We'll keep it though, in the hope that we can mend EntityService.
|
||||
public class DictionaryItemTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public DictionaryItemTreeControllerBase(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService) =>
|
||||
LocalizationService = localizationService;
|
||||
|
||||
// dictionary items do not currently have a known UmbracoObjectType, so we'll settle with Unknown for now
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.Unknown;
|
||||
|
||||
protected ILocalizationService LocalizationService { get; }
|
||||
|
||||
protected EntityTreeItemViewModel[] MapTreeItemViewModels(Guid? parentKey, IDictionaryItem[] dictionaryItems)
|
||||
=> dictionaryItems.Select(dictionaryItem => new EntityTreeItemViewModel
|
||||
{
|
||||
Icon = Constants.Icons.RelationType,
|
||||
Name = dictionaryItem.ItemKey,
|
||||
Key = dictionaryItem.Key,
|
||||
Type = Constants.UdiEntityType.DictionaryItem,
|
||||
HasChildren = false,
|
||||
IsContainer = LocalizationService.GetDictionaryItemChildren(dictionaryItem.Key).Any(),
|
||||
ParentKey = parentKey
|
||||
}).ToArray();
|
||||
|
||||
// localization service does not (yet) allow pagination of dictionary items, we have to do it in memory for now
|
||||
protected IDictionaryItem[] PaginatedDictionaryItems(long pageNumber, int pageSize, IEnumerable<IDictionaryItem> allDictionaryItems, out long totalItems)
|
||||
{
|
||||
IDictionaryItem[] allDictionaryItemsAsArray = allDictionaryItems.ToArray();
|
||||
|
||||
totalItems = allDictionaryItemsAsArray.Length;
|
||||
return allDictionaryItemsAsArray
|
||||
.OrderBy(item => item.ItemKey)
|
||||
.Skip((int)pageNumber * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
public class ItemsDictionaryItemTreeController : DictionaryItemTreeControllerBase
|
||||
{
|
||||
public ItemsDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService, localizationService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
{
|
||||
IDictionaryItem[] dictionaryItems = LocalizationService.GetDictionaryItemsByIds(keys).ToArray();
|
||||
|
||||
EntityTreeItemViewModel[] viewModels = MapTreeItemViewModels(null, dictionaryItems);
|
||||
|
||||
return await Task.FromResult(Ok(viewModels));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Paging;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree;
|
||||
|
||||
public class RootDictionaryItemTreeController : DictionaryItemTreeControllerBase
|
||||
{
|
||||
public RootDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService)
|
||||
: base(entityService, localizationService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<EntityTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
{
|
||||
if (PaginationService.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize, out ProblemDetails? error) == false)
|
||||
{
|
||||
return BadRequest(error);
|
||||
}
|
||||
|
||||
IDictionaryItem[] dictionaryItems = PaginatedDictionaryItems(
|
||||
pageNumber,
|
||||
pageSize,
|
||||
LocalizationService.GetRootDictionaryItems(),
|
||||
out var totalItems);
|
||||
|
||||
EntityTreeItemViewModel[] viewModels = MapTreeItemViewModels(null, dictionaryItems);
|
||||
|
||||
PagedViewModel<EntityTreeItemViewModel> result = PagedViewModel(viewModels, totalItems);
|
||||
return await Task.FromResult(Ok(result));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin;
|
||||
|
||||
public class ChildrenDocumentRecycleBinController : DocumentRecycleBinControllerBase
|
||||
{
|
||||
public ChildrenDocumentRecycleBinController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RecycleBinItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RecycleBinItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100)
|
||||
=> await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.RecycleBin;
|
||||
using Umbraco.Cms.ManagementApi.Filters;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/recycle-bin")]
|
||||
[RequireDocumentTreeRootAccess]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Document))]
|
||||
public class DocumentRecycleBinControllerBase : RecycleBinControllerBase<RecycleBinItemViewModel>
|
||||
{
|
||||
public DocumentRecycleBinControllerBase(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.Document;
|
||||
|
||||
protected override int RecycleBinRootId => Constants.System.RecycleBinContent;
|
||||
|
||||
protected override RecycleBinItemViewModel MapRecycleBinViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
RecycleBinItemViewModel viewModel = base.MapRecycleBinViewModel(parentKey, entity);
|
||||
|
||||
if (entity is IDocumentEntitySlim documentEntitySlim)
|
||||
{
|
||||
viewModel.Icon = documentEntitySlim.ContentTypeIcon ?? viewModel.Icon;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin;
|
||||
|
||||
public class RootDocumentRecycleBinController : DocumentRecycleBinControllerBase
|
||||
{
|
||||
public RootDocumentRecycleBinController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RecycleBinItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RecycleBinItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree;
|
||||
|
||||
public class ChildrenDocumentTreeController : DocumentTreeControllerBase
|
||||
{
|
||||
public ChildrenDocumentTreeController(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
IPublicAccessService publicAccessService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService, publicAccessService, appCaches, backofficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<DocumentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<DocumentTreeItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100, Guid? dataTypeKey = null, string? culture = null)
|
||||
{
|
||||
IgnoreUserStartNodesForDataType(dataTypeKey);
|
||||
RenderForClientCulture(culture);
|
||||
return await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Document))]
|
||||
public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBase<DocumentTreeItemViewModel>
|
||||
{
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private string? _culture;
|
||||
|
||||
protected DocumentTreeControllerBase(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
IPublicAccessService publicAccessService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService)
|
||||
{
|
||||
_publicAccessService = publicAccessService;
|
||||
_appCaches = appCaches;
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.Document;
|
||||
|
||||
protected override Ordering ItemOrdering => Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.SortOrder));
|
||||
|
||||
protected void RenderForClientCulture(string? culture) => _culture = culture;
|
||||
|
||||
protected override DocumentTreeItemViewModel MapTreeItemViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
DocumentTreeItemViewModel viewModel = base.MapTreeItemViewModel(parentKey, entity);
|
||||
|
||||
if (entity is IDocumentEntitySlim documentEntitySlim)
|
||||
{
|
||||
viewModel.IsPublished = documentEntitySlim.Published;
|
||||
viewModel.IsEdited = documentEntitySlim.Edited;
|
||||
viewModel.Icon = documentEntitySlim.ContentTypeIcon ?? viewModel.Icon;
|
||||
viewModel.IsProtected = _publicAccessService.IsProtected(entity.Path);
|
||||
|
||||
if (_culture != null && documentEntitySlim.Variations.VariesByCulture())
|
||||
{
|
||||
viewModel.Name = documentEntitySlim.CultureNames.TryGetValue(_culture, out var cultureName)
|
||||
? cultureName
|
||||
: $"({viewModel.Name})";
|
||||
|
||||
viewModel.IsPublished = documentEntitySlim.PublishedCultures.Contains(_culture);
|
||||
viewModel.IsEdited = documentEntitySlim.EditedCultures.Contains(_culture);
|
||||
}
|
||||
|
||||
viewModel.IsEdited &= viewModel.IsPublished;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
// TODO: delete these (faking start node setup for unlimited editor)
|
||||
protected override int[] GetUserStartNodeIds() => new[] { -1 };
|
||||
|
||||
protected override string[] GetUserStartNodePaths() => Array.Empty<string>();
|
||||
|
||||
// TODO: use these implementations instead of the dummy ones above once we have backoffice auth in place
|
||||
// protected override int[] GetUserStartNodeIds()
|
||||
// => _backofficeSecurityAccessor
|
||||
// .BackOfficeSecurity?
|
||||
// .CurrentUser?
|
||||
// .CalculateContentStartNodeIds(EntityService, _appCaches)
|
||||
// ?? Array.Empty<int>();
|
||||
//
|
||||
// protected override string[] GetUserStartNodePaths()
|
||||
// => _backofficeSecurityAccessor
|
||||
// .BackOfficeSecurity?
|
||||
// .CurrentUser?
|
||||
// .GetContentStartNodePaths(EntityService, _appCaches)
|
||||
// ?? Array.Empty<string>();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree;
|
||||
|
||||
public class ItemsDocumentTreeController : DocumentTreeControllerBase
|
||||
{
|
||||
public ItemsDocumentTreeController(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
IPublicAccessService publicAccessService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService, publicAccessService, appCaches, backofficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<DocumentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DocumentTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null, string? culture = null)
|
||||
{
|
||||
IgnoreUserStartNodesForDataType(dataTypeKey);
|
||||
RenderForClientCulture(culture);
|
||||
return await GetItems(keys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree;
|
||||
|
||||
public class RootDocumentTreeController : DocumentTreeControllerBase
|
||||
{
|
||||
public RootDocumentTreeController(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
IPublicAccessService publicAccessService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService, publicAccessService, appCaches, backofficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<DocumentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<DocumentTreeItemViewModel>>> Root(int skip = 0, int take = 100, Guid? dataTypeKey = null, string? culture = null)
|
||||
{
|
||||
IgnoreUserStartNodesForDataType(dataTypeKey);
|
||||
RenderForClientCulture(culture);
|
||||
return await GetRoot(skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentBlueprint}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DocumentBlueprint))]
|
||||
public class DocumentBlueprintTreeControllerBase : EntityTreeControllerBase<DocumentBlueprintTreeItemViewModel>
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public DocumentBlueprintTreeControllerBase(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService) =>
|
||||
_contentTypeService = contentTypeService;
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.DocumentBlueprint;
|
||||
|
||||
protected override DocumentBlueprintTreeItemViewModel[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities)
|
||||
{
|
||||
var contentTypeAliases = entities
|
||||
.OfType<IDocumentEntitySlim>()
|
||||
.Select(entity => entity.ContentTypeAlias)
|
||||
.ToArray();
|
||||
|
||||
var contentTypeIds = _contentTypeService.GetAllContentTypeIds(contentTypeAliases).ToArray();
|
||||
var contentTypeByAlias = _contentTypeService
|
||||
.GetAll(contentTypeIds)
|
||||
.ToDictionary(contentType => contentType.Alias);
|
||||
|
||||
return entities.Select(entity =>
|
||||
{
|
||||
DocumentBlueprintTreeItemViewModel viewModel = base.MapTreeItemViewModel(parentKey, entity);
|
||||
viewModel.Icon = Constants.Icons.Blueprint;
|
||||
viewModel.HasChildren = false;
|
||||
|
||||
if (entity is IDocumentEntitySlim documentEntitySlim
|
||||
&& contentTypeByAlias.TryGetValue(documentEntitySlim.ContentTypeAlias, out IContentType? contentType))
|
||||
{
|
||||
viewModel.DocumentTypeKey = contentType.Key;
|
||||
viewModel.DocumentTypeAlias = contentType.Alias;
|
||||
viewModel.DocumentTypeName = contentType.Name;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree;
|
||||
|
||||
public class ItemsDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
|
||||
{
|
||||
public ItemsDocumentBlueprintTreeController(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService, contentTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<DocumentBlueprintTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DocumentBlueprintTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
=> await GetItems(keys);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree;
|
||||
|
||||
public class RootDocumentBlueprintTreeController : DocumentBlueprintTreeControllerBase
|
||||
{
|
||||
public RootDocumentBlueprintTreeController(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService, contentTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<DocumentBlueprintTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<DocumentBlueprintTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree;
|
||||
|
||||
public class ChildrenDocumentTypeTreeController : DocumentTypeTreeControllerBase
|
||||
{
|
||||
public ChildrenDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService, contentTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<DocumentTypeTreeItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100, bool foldersOnly = false)
|
||||
{
|
||||
RenderFoldersOnly(foldersOnly);
|
||||
return await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.DocumentType))]
|
||||
public class DocumentTypeTreeControllerBase : FolderTreeControllerBase<DocumentTypeTreeItemViewModel>
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public DocumentTypeTreeControllerBase(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService) =>
|
||||
_contentTypeService = contentTypeService;
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.DocumentType;
|
||||
|
||||
protected override UmbracoObjectTypes FolderObjectType => UmbracoObjectTypes.DocumentTypeContainer;
|
||||
|
||||
protected override DocumentTypeTreeItemViewModel[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities)
|
||||
{
|
||||
var contentTypes = _contentTypeService
|
||||
.GetAll(entities.Select(entity => entity.Id).ToArray())
|
||||
.ToDictionary(contentType => contentType.Id);
|
||||
|
||||
return entities.Select(entity =>
|
||||
{
|
||||
DocumentTypeTreeItemViewModel viewModel = MapTreeItemViewModel(parentKey, entity);
|
||||
if (contentTypes.TryGetValue(entity.Id, out IContentType? contentType))
|
||||
{
|
||||
viewModel.Icon = contentType.Icon ?? viewModel.Icon;
|
||||
viewModel.IsElement = contentType.IsElement;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree;
|
||||
|
||||
public class ItemsDocumentTypeTreeController : DocumentTypeTreeControllerBase
|
||||
{
|
||||
public ItemsDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService, contentTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<DocumentTypeTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DocumentTypeTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
=> await GetItems(keys);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree;
|
||||
|
||||
public class RootDocumentTypeTreeController : DocumentTypeTreeControllerBase
|
||||
{
|
||||
public RootDocumentTypeTreeController(IEntityService entityService, IContentTypeService contentTypeService)
|
||||
: base(entityService, contentTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<DocumentTypeTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<DocumentTypeTreeItemViewModel>>> Root(int skip = 0, int take = 100, bool foldersOnly = false)
|
||||
{
|
||||
RenderFoldersOnly(foldersOnly);
|
||||
return await GetRoot(skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/examineManagement")]
|
||||
[OpenApiTag("ExamineManagement")]
|
||||
public class ExamineManagementControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class IndexExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineIndexViewModelFactory _examineIndexViewModelFactory;
|
||||
private readonly IExamineManager _examineManager;
|
||||
|
||||
public IndexExamineManagementController(
|
||||
IExamineIndexViewModelFactory examineIndexViewModelFactory,
|
||||
IExamineManager examineManager)
|
||||
{
|
||||
_examineIndexViewModelFactory = examineIndexViewModelFactory;
|
||||
_examineManager = examineManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the index has been rebuilt
|
||||
/// </summary>
|
||||
/// <param name="indexName"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is kind of rudimentary since there's no way we can know that the index has rebuilt, we
|
||||
/// have a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache
|
||||
/// </remarks>
|
||||
[HttpGet("index")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ExamineIndexViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<ExamineIndexViewModel?>> Index(string indexName)
|
||||
{
|
||||
if (_examineManager.TryGetIndex(indexName, out IIndex? index))
|
||||
{
|
||||
return await Task.FromResult(_examineIndexViewModelFactory.Create(index!));
|
||||
}
|
||||
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Index Not Found",
|
||||
Detail = $"No index found with name = {indexName}",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return await Task.FromResult(BadRequest(invalidModelProblem));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class IndexesExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly IExamineIndexViewModelFactory _examineIndexViewModelFactory;
|
||||
|
||||
public IndexesExamineManagementController(
|
||||
IExamineManager examineManager,
|
||||
IExamineIndexViewModelFactory examineIndexViewModelFactory)
|
||||
{
|
||||
_examineManager = examineManager;
|
||||
_examineIndexViewModelFactory = examineIndexViewModelFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the details for indexers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("indexes")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ExamineIndexViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<ExamineIndexViewModel>> Indexes(int skip, int take)
|
||||
{
|
||||
ExamineIndexViewModel[] indexes = _examineManager.Indexes
|
||||
.Select(_examineIndexViewModelFactory.Create)
|
||||
.OrderBy(examineIndexModel => examineIndexModel.Name?.TrimEnd("Indexer")).ToArray();
|
||||
|
||||
var viewModel = new PagedViewModel<ExamineIndexViewModel> { Items = indexes.Skip(skip).Take(take), Total = indexes.Length };
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.New.Cms.Infrastructure.Services;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class RebuildExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly ILogger<RebuildExamineManagementController> _logger;
|
||||
private readonly IIndexingRebuilderService _indexingRebuilderService;
|
||||
private readonly IExamineManager _examineManager;
|
||||
|
||||
public RebuildExamineManagementController(
|
||||
ILogger<RebuildExamineManagementController> logger,
|
||||
IIndexingRebuilderService indexingRebuilderService,
|
||||
IExamineManager examineManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_indexingRebuilderService = indexingRebuilderService;
|
||||
_examineManager = examineManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the index
|
||||
/// </summary>
|
||||
/// <param name="indexName"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("rebuild")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(OkResult), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Rebuild(string indexName)
|
||||
{
|
||||
if (!_examineManager.TryGetIndex(indexName, out var index))
|
||||
{
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Index Not Found",
|
||||
Detail = $"No index found with name = {indexName}",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return await Task.FromResult(BadRequest(invalidModelProblem));
|
||||
}
|
||||
|
||||
if (!_indexingRebuilderService.CanRebuild(index.Name))
|
||||
{
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Could not validate the populator",
|
||||
Detail =
|
||||
$"The index {index?.Name} could not be rebuilt because we could not validate its associated {typeof(IIndexPopulator)}",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return await Task.FromResult(BadRequest(invalidModelProblem));
|
||||
}
|
||||
|
||||
_logger.LogInformation("Rebuilding index '{IndexName}'", indexName);
|
||||
|
||||
if (_indexingRebuilderService.TryRebuild(index, indexName))
|
||||
{
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
|
||||
var problemDetails = new ProblemDetails
|
||||
{
|
||||
Title = "Index could not be rebuilt",
|
||||
Detail = $"The index {index.Name} could not be rebuild. Check the log for details on this error.",
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return await Task.FromResult(Conflict(problemDetails));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Examine;
|
||||
using Examine.Search;
|
||||
using Lucene.Net.QueryParsers.Classic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class SearchExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineManagerService _examineManagerService;
|
||||
|
||||
public SearchExamineManagementController(IExamineManagerService examineManagerService) => _examineManagerService = examineManagerService;
|
||||
|
||||
[HttpGet("search")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<PagedViewModel<SearchResultViewModel>>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<PagedViewModel<SearchResultViewModel>>> GetSearchResults(string searcherName, string? query, int skip, int take)
|
||||
{
|
||||
query = query?.Trim();
|
||||
|
||||
if (query.IsNullOrWhiteSpace())
|
||||
{
|
||||
return new PagedViewModel<SearchResultViewModel>();
|
||||
}
|
||||
|
||||
if (!_examineManagerService.TryFindSearcher(searcherName, out ISearcher searcher))
|
||||
{
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Could not find a valid searcher",
|
||||
Detail = "The provided searcher name did not match any of our registered searchers",
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return NotFound(invalidModelProblem);
|
||||
}
|
||||
|
||||
ISearchResults results;
|
||||
|
||||
// NativeQuery will work for a single word/phrase too (but depends on the implementation) the lucene one will work.
|
||||
try
|
||||
{
|
||||
results = searcher
|
||||
.CreateQuery()
|
||||
.NativeQuery(query)
|
||||
.Execute(QueryOptions.SkipTake(skip, take));
|
||||
}
|
||||
catch (ParseException)
|
||||
{
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
{
|
||||
Title = "Could not parse the query",
|
||||
Detail = "Parser could not parse the query. Please double check if the query is valid. Sometimes this can also happen if your query starts with a wildcard (*)",
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return BadRequest(invalidModelProblem);
|
||||
}
|
||||
|
||||
return await Task.FromResult(new PagedViewModel<SearchResultViewModel>
|
||||
{
|
||||
Total = results.TotalItemCount,
|
||||
Items = results.Select(x => new SearchResultViewModel
|
||||
{
|
||||
Id = x.Id,
|
||||
Score = x.Score,
|
||||
Fields = x.AllValues.OrderBy(y => y.Key).Select(y => new FieldViewModel { Name = y.Key, Values = y.Value }),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Examine;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.ExamineManagement;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.ExamineManagement;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
public class SearchersExamineManagementController : ExamineManagementControllerBase
|
||||
{
|
||||
private readonly IExamineManager _examineManager;
|
||||
|
||||
public SearchersExamineManagementController(IExamineManager examineManager) => _examineManager = examineManager;
|
||||
|
||||
/// <summary>
|
||||
/// Get the details for searchers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("searchers")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<SearcherViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<SearcherViewModel>>> Searchers(int skip, int take)
|
||||
{
|
||||
var searchers = new List<SearcherViewModel>(
|
||||
_examineManager.RegisteredSearchers.Select(searcher => new SearcherViewModel { Name = searcher.Name })
|
||||
.OrderBy(x =>
|
||||
x.Name.TrimEnd("Searcher"))); // order by name , but strip the "Searcher" from the end if it exists
|
||||
var viewModel = new PagedViewModel<SearcherViewModel>
|
||||
{
|
||||
Items = searchers.Skip(skip).Take(take),
|
||||
Total = searchers.Count,
|
||||
};
|
||||
|
||||
return await Task.FromResult(Ok(viewModel));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.ManagementApi.Builders;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Help;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Help;
|
||||
|
||||
public class GetHelpController : HelpControllerBase
|
||||
{
|
||||
private readonly ILogger<GetHelpController> _logger;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private HelpPageSettings _helpPageSettings;
|
||||
|
||||
public GetHelpController(
|
||||
IOptionsMonitor<HelpPageSettings> helpPageSettings,
|
||||
ILogger<GetHelpController> logger,
|
||||
IJsonSerializer jsonSerializer)
|
||||
{
|
||||
_logger = logger;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_helpPageSettings = helpPageSettings.CurrentValue;
|
||||
helpPageSettings.OnChange(UpdateHelpPageSettings);
|
||||
}
|
||||
|
||||
private void UpdateHelpPageSettings(HelpPageSettings settings) => _helpPageSettings = settings;
|
||||
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(PagedViewModel<HelpPageViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> Get(string section, string? tree, int skip, int take, string? baseUrl = "https://our.umbraco.com")
|
||||
{
|
||||
if (IsAllowedUrl(baseUrl) is false)
|
||||
{
|
||||
_logger.LogError($"The following URL is not listed in the allowlist for HelpPage in HelpPageSettings: {baseUrl}");
|
||||
|
||||
ProblemDetails invalidModelProblem =
|
||||
new ProblemDetailsBuilder()
|
||||
.WithTitle("Invalid database configuration")
|
||||
.WithDetail("The provided database configuration is invalid")
|
||||
.Build();
|
||||
|
||||
return BadRequest(invalidModelProblem);
|
||||
}
|
||||
|
||||
var url = string.Format(baseUrl + "/Umbraco/Documentation/Lessons/GetContextHelpDocs?sectionAlias={0}&treeAlias={1}", section, tree);
|
||||
|
||||
try
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
|
||||
// fetch dashboard json and parse to JObject
|
||||
var json = await httpClient.GetStringAsync(url);
|
||||
List<HelpPageViewModel>? result = _jsonSerializer.Deserialize<List<HelpPageViewModel>>(json);
|
||||
if (result != null)
|
||||
{
|
||||
return Ok(new PagedViewModel<HelpPageViewModel>
|
||||
{
|
||||
Total = result.Count,
|
||||
Items = result.Skip(skip).Take(take),
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException rex)
|
||||
{
|
||||
_logger.LogInformation($"Check your network connection, exception: {rex.Message}");
|
||||
}
|
||||
|
||||
return Ok(PagedViewModel<HelpPageViewModel>.Empty());
|
||||
}
|
||||
|
||||
private bool IsAllowedUrl(string? url) =>
|
||||
_helpPageSettings.HelpPageUrlAllowList is null || _helpPageSettings.HelpPageUrlAllowList.Contains(url);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Help;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/help")]
|
||||
[OpenApiTag("Help")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class HelpControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -7,9 +7,9 @@ using Umbraco.New.Cms.Web.Common.Routing;
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Install;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/install")]
|
||||
[VersionedApiBackOfficeRoute("install")]
|
||||
[OpenApiTag("Install")]
|
||||
[RequireRuntimeLevel(RuntimeLevel.Install)]
|
||||
public abstract class InstallControllerBase : Controller
|
||||
public abstract class InstallControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
|
||||
@@ -27,16 +27,13 @@ public class ValidateDatabaseInstallController : InstallControllerBase
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> ValidateDatabase(DatabaseInstallViewModel viewModel)
|
||||
{
|
||||
// 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),
|
||||
// But we need to figure out how we want to handle it in the meantime? use Task.FromResult or?
|
||||
DatabaseModel databaseModel = _mapper.Map<DatabaseModel>(viewModel)!;
|
||||
|
||||
var success = _databaseBuilder.ConfigureDatabaseConnection(databaseModel, true);
|
||||
|
||||
if (success)
|
||||
{
|
||||
return Ok();
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
|
||||
var invalidModelProblem = new ProblemDetails
|
||||
@@ -47,6 +44,6 @@ public class ValidateDatabaseInstallController : InstallControllerBase
|
||||
Type = "Error",
|
||||
};
|
||||
|
||||
return BadRequest(invalidModelProblem);
|
||||
return await Task.FromResult(BadRequest(invalidModelProblem));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Language;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
public class AllLanguageController : LanguageControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public AllLanguageController(ILocalizationService localizationService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
/// <summary>1
|
||||
/// Returns all currently configured languages.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<LanguageViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<LanguageViewModel>?> GetAll(int skip, int take)
|
||||
{
|
||||
PagedModel<ILanguage> allLanguages = _localizationService.GetAllLanguagesPaged(skip, take);
|
||||
|
||||
return await Task.FromResult(_umbracoMapper.Map<PagedModel<ILanguage>, PagedViewModel<LanguageViewModel>>(allLanguages)!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Language;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
public class ByIdLanguageController : LanguageControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
|
||||
public ByIdLanguageController(ILocalizationService localizationService, IUmbracoMapper umbracoMapper)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<LanguageViewModel?>> ById(int id)
|
||||
{
|
||||
ILanguage? lang = _localizationService.GetLanguageById(id);
|
||||
if (lang is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await Task.FromResult(_umbracoMapper.Map<LanguageViewModel>(lang));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Language;
|
||||
using Umbraco.New.Cms.Core.Services.Installer;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
public class CreateLanguageController : LanguageControllerBase
|
||||
{
|
||||
private readonly ILanguageService _languageService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public CreateLanguageController(ILanguageService languageService, IUmbracoMapper umbracoMapper, ILocalizationService localizationService)
|
||||
{
|
||||
_languageService = languageService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or saves a language
|
||||
/// </summary>
|
||||
[HttpPost("create")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
// TODO: This needs to be an authorized endpoint.
|
||||
public async Task<ActionResult> Create(LanguageViewModel language)
|
||||
{
|
||||
if (_languageService.LanguageAlreadyExists(language.Id, language.IsoCode))
|
||||
{
|
||||
// Someone is trying to create a language that already exist
|
||||
ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists");
|
||||
return ValidationProblem(ModelState);
|
||||
}
|
||||
|
||||
// Creating a new lang...
|
||||
CultureInfo culture;
|
||||
try
|
||||
{
|
||||
culture = CultureInfo.GetCultureInfo(language.IsoCode);
|
||||
}
|
||||
catch (CultureNotFoundException)
|
||||
{
|
||||
ModelState.AddModelError("IsoCode", "No Culture found with name " + language.IsoCode);
|
||||
return ValidationProblem(ModelState);
|
||||
}
|
||||
|
||||
language.Name ??= culture.EnglishName;
|
||||
|
||||
ILanguage newLang = _umbracoMapper.Map<ILanguage>(language)!;
|
||||
|
||||
_localizationService.Save(newLang);
|
||||
return await Task.FromResult(Created($"api/v1.0/language/{newLang.Id}", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Builders;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
public class DeleteLanguageController : LanguageControllerBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public DeleteLanguageController(ILocalizationService localizationService) => _localizationService = localizationService;
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a language with a given ID
|
||||
/// </summary>
|
||||
[HttpDelete("{id:int}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
// TODO: This needs to be an authorized endpoint.
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
ILanguage? language = _localizationService.GetLanguageById(id);
|
||||
if (language == null)
|
||||
{
|
||||
return await Task.FromResult(NotFound());
|
||||
}
|
||||
|
||||
// the service would not let us do it, but test here nevertheless
|
||||
if (language.IsDefault)
|
||||
{
|
||||
ProblemDetails invalidModelProblem =
|
||||
new ProblemDetailsBuilder()
|
||||
.WithTitle("Cannot delete default language")
|
||||
.WithDetail($"Language '{language.IsoCode}' is currently set to 'default' and can not be deleted.")
|
||||
.Build();
|
||||
|
||||
return BadRequest(invalidModelProblem);
|
||||
}
|
||||
|
||||
// service is happy deleting a language that's fallback for another language,
|
||||
// will just remove it - so no need to check here
|
||||
_localizationService.Delete(language);
|
||||
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/language")]
|
||||
[OpenApiTag("Language")]
|
||||
[ApiVersion("1.0")]
|
||||
public abstract class LanguageControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Language;
|
||||
using Umbraco.New.Cms.Core.Services.Installer;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Language;
|
||||
|
||||
public class UpdateLanguageController : LanguageControllerBase
|
||||
{
|
||||
private readonly ILanguageService _languageService;
|
||||
private readonly IUmbracoMapper _umbracoMapper;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public UpdateLanguageController(ILanguageService languageService, IUmbracoMapper umbracoMapper, ILocalizationService localizationService)
|
||||
{
|
||||
_languageService = languageService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a language
|
||||
/// </summary>
|
||||
[HttpPut("update")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
// TODO: This needs to be an authorized endpoint.
|
||||
public async Task<ActionResult> Update(LanguageViewModel language)
|
||||
{
|
||||
ILanguage? existingById = language.Id != default ? _localizationService.GetLanguageById(language.Id) : null;
|
||||
if (existingById is null)
|
||||
{
|
||||
return await Task.FromResult(NotFound());
|
||||
}
|
||||
|
||||
// note that the service will prevent the default language from being "un-defaulted"
|
||||
// but does not hurt to test here - though the UI should prevent it too
|
||||
if (existingById.IsDefault && !language.IsDefault)
|
||||
{
|
||||
ModelState.AddModelError("IsDefault", "Cannot un-default the default language.");
|
||||
return await Task.FromResult(ValidationProblem(ModelState));
|
||||
}
|
||||
|
||||
existingById = _umbracoMapper.Map(language, existingById);
|
||||
|
||||
if (!_languageService.CanUseLanguagesFallbackLanguage(existingById))
|
||||
{
|
||||
ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist.");
|
||||
return await Task.FromResult(ValidationProblem(ModelState));
|
||||
}
|
||||
|
||||
if (!_languageService.CanGetProperFallbackLanguage(existingById))
|
||||
{
|
||||
ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {_localizationService.GetLanguageById(existingById.FallbackLanguageId!.Value)} would create a circular path.");
|
||||
return await Task.FromResult(ValidationProblem(ModelState));
|
||||
}
|
||||
|
||||
_localizationService.Save(existingById);
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.ManagementApi.Filters;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers;
|
||||
|
||||
[ManagementApiJsonConfiguration]
|
||||
public class ManagementApiControllerBase : Controller
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin;
|
||||
|
||||
public class ChildrenMediaRecycleBinController : MediaRecycleBinControllerBase
|
||||
{
|
||||
public ChildrenMediaRecycleBinController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RecycleBinItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RecycleBinItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100)
|
||||
=> await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.RecycleBin;
|
||||
using Umbraco.Cms.ManagementApi.Filters;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/recycle-bin")]
|
||||
[RequireMediaTreeRootAccess]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Media))]
|
||||
public class MediaRecycleBinControllerBase : RecycleBinControllerBase<RecycleBinItemViewModel>
|
||||
{
|
||||
public MediaRecycleBinControllerBase(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.Media;
|
||||
|
||||
protected override int RecycleBinRootId => Constants.System.RecycleBinMedia;
|
||||
|
||||
protected override RecycleBinItemViewModel MapRecycleBinViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
RecycleBinItemViewModel viewModel = base.MapRecycleBinViewModel(parentKey, entity);
|
||||
|
||||
if (entity is IMediaEntitySlim mediaEntitySlim)
|
||||
{
|
||||
viewModel.Icon = mediaEntitySlim.ContentTypeIcon ?? viewModel.Icon;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin;
|
||||
|
||||
public class RootMediaRecycleBinController : MediaRecycleBinControllerBase
|
||||
{
|
||||
public RootMediaRecycleBinController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RecycleBinItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<RecycleBinItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree;
|
||||
|
||||
public class ChildrenMediaTreeController : MediaTreeControllerBase
|
||||
{
|
||||
public ChildrenMediaTreeController(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ContentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<ContentTreeItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100, Guid? dataTypeKey = null)
|
||||
{
|
||||
IgnoreUserStartNodesForDataType(dataTypeKey);
|
||||
return await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree;
|
||||
|
||||
public class ItemsMediaTreeController : MediaTreeControllerBase
|
||||
{
|
||||
public ItemsMediaTreeController(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<ContentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<ContentTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null)
|
||||
{
|
||||
IgnoreUserStartNodesForDataType(dataTypeKey);
|
||||
return await GetItems(keys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Media))]
|
||||
public class MediaTreeControllerBase : UserStartNodeTreeControllerBase<ContentTreeItemViewModel>
|
||||
{
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
|
||||
public MediaTreeControllerBase(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService)
|
||||
{
|
||||
_appCaches = appCaches;
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.Media;
|
||||
|
||||
protected override Ordering ItemOrdering => Ordering.By(nameof(Infrastructure.Persistence.Dtos.NodeDto.SortOrder));
|
||||
|
||||
protected override ContentTreeItemViewModel MapTreeItemViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
ContentTreeItemViewModel viewModel = base.MapTreeItemViewModel(parentKey, entity);
|
||||
|
||||
if (entity is IMediaEntitySlim mediaEntitySlim)
|
||||
{
|
||||
viewModel.Icon = mediaEntitySlim.ContentTypeIcon ?? viewModel.Icon;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
// TODO: delete these (faking start node setup for unlimited editor)
|
||||
protected override int[] GetUserStartNodeIds() => new[] { -1 };
|
||||
|
||||
protected override string[] GetUserStartNodePaths() => Array.Empty<string>();
|
||||
|
||||
// TODO: use these implementations instead of the dummy ones above once we have backoffice auth in place
|
||||
// protected override int[] GetUserStartNodeIds()
|
||||
// => _backofficeSecurityAccessor
|
||||
// .BackOfficeSecurity?
|
||||
// .CurrentUser?
|
||||
// .CalculateMediaStartNodeIds(EntityService, _appCaches)
|
||||
// ?? Array.Empty<int>();
|
||||
//
|
||||
// protected override string[] GetUserStartNodePaths()
|
||||
// => _backofficeSecurityAccessor
|
||||
// .BackOfficeSecurity?
|
||||
// .CurrentUser?
|
||||
// .GetMediaStartNodePaths(EntityService, _appCaches)
|
||||
// ?? Array.Empty<string>();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Entities;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree;
|
||||
|
||||
public class RootMediaTreeController : MediaTreeControllerBase
|
||||
{
|
||||
public RootMediaTreeController(
|
||||
IEntityService entityService,
|
||||
IUserStartNodeEntitiesService userStartNodeEntitiesService,
|
||||
IDataTypeService dataTypeService,
|
||||
AppCaches appCaches,
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor)
|
||||
: base(entityService, userStartNodeEntitiesService, dataTypeService, appCaches, backofficeSecurityAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<ContentTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<ContentTreeItemViewModel>>> Root(int skip = 0, int take = 100, Guid? dataTypeKey = null)
|
||||
{
|
||||
IgnoreUserStartNodesForDataType(dataTypeKey);
|
||||
return await GetRoot(skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree;
|
||||
|
||||
public class ChildrenMediaTypeTreeController : MediaTypeTreeControllerBase
|
||||
{
|
||||
public ChildrenMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
|
||||
: base(entityService, mediaTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FolderTreeItemViewModel>>> Children(Guid parentKey, int skip = 0, int take = 100, bool foldersOnly = false)
|
||||
{
|
||||
RenderFoldersOnly(foldersOnly);
|
||||
return await GetChildren(parentKey, skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree;
|
||||
|
||||
public class ItemsMediaTypeTreeController : MediaTypeTreeControllerBase
|
||||
{
|
||||
public ItemsMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
|
||||
: base(entityService, mediaTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
=> await GetItems(keys);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MediaType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.MediaType))]
|
||||
public class MediaTypeTreeControllerBase : FolderTreeControllerBase<FolderTreeItemViewModel>
|
||||
{
|
||||
private readonly IMediaTypeService _mediaTypeService;
|
||||
|
||||
public MediaTypeTreeControllerBase(IEntityService entityService, IMediaTypeService mediaTypeService)
|
||||
: base(entityService) =>
|
||||
_mediaTypeService = mediaTypeService;
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.MediaType;
|
||||
|
||||
protected override UmbracoObjectTypes FolderObjectType => UmbracoObjectTypes.MediaTypeContainer;
|
||||
|
||||
protected override FolderTreeItemViewModel[] MapTreeItemViewModels(Guid? parentKey, IEntitySlim[] entities)
|
||||
{
|
||||
var mediaTypes = _mediaTypeService
|
||||
.GetAll(entities.Select(entity => entity.Id).ToArray())
|
||||
.ToDictionary(contentType => contentType.Id);
|
||||
|
||||
return entities.Select(entity =>
|
||||
{
|
||||
FolderTreeItemViewModel viewModel = MapTreeItemViewModel(parentKey, entity);
|
||||
if (mediaTypes.TryGetValue(entity.Id, out IMediaType? mediaType))
|
||||
{
|
||||
viewModel.Icon = mediaType.Icon ?? viewModel.Icon;
|
||||
}
|
||||
|
||||
return viewModel;
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree;
|
||||
|
||||
public class RootMediaTypeTreeController : MediaTypeTreeControllerBase
|
||||
{
|
||||
public RootMediaTypeTreeController(IEntityService entityService, IMediaTypeService mediaTypeService)
|
||||
: base(entityService, mediaTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FolderTreeItemViewModel>>> Root(int skip = 0, int take = 100, bool foldersOnly = false)
|
||||
{
|
||||
RenderFoldersOnly(foldersOnly);
|
||||
return await GetRoot(skip, take);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree;
|
||||
|
||||
public class ItemsMemberGroupTreeController : MemberGroupTreeControllerBase
|
||||
{
|
||||
public ItemsMemberGroupTreeController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<EntityTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
=> await GetItems(keys);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberGroup}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.MemberGroup))]
|
||||
public class MemberGroupTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public MemberGroupTreeControllerBase(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.MemberGroup;
|
||||
|
||||
protected override EntityTreeItemViewModel MapTreeItemViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
EntityTreeItemViewModel viewModel = base.MapTreeItemViewModel(parentKey, entity);
|
||||
viewModel.Icon = Constants.Icons.MemberGroup;
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree;
|
||||
|
||||
public class RootMemberGroupTreeController : MemberGroupTreeControllerBase
|
||||
{
|
||||
public RootMemberGroupTreeController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<EntityTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree;
|
||||
|
||||
public class ItemsMemberTypeTreeController : MemberTypeTreeControllerBase
|
||||
{
|
||||
public ItemsMemberTypeTreeController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<EntityTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
=> await GetItems(keys);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.MemberType))]
|
||||
public class MemberTypeTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public MemberTypeTreeControllerBase(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.MemberType;
|
||||
|
||||
protected override EntityTreeItemViewModel MapTreeItemViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
EntityTreeItemViewModel viewModel = base.MapTreeItemViewModel(parentKey, entity);
|
||||
viewModel.Icon = Constants.Icons.User;
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree;
|
||||
|
||||
public class RootMemberTypeTreeController : MemberTypeTreeControllerBase
|
||||
{
|
||||
public RootMemberTypeTreeController(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<EntityTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree;
|
||||
|
||||
public class ChildrenPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
public ChildrenPartialViewTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FileSystemTreeItemViewModel>>> Children(string path, int skip = 0, int take = 100)
|
||||
=> await GetChildren(path, skip, take);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree;
|
||||
|
||||
public class ItemsPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
public ItemsPartialViewTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FileSystemTreeItemViewModel>>> Items([FromQuery(Name = "path")] string[] paths)
|
||||
=> await GetItems(paths);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.PartialView}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.PartialView))]
|
||||
public class PartialViewTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
public PartialViewTreeControllerBase(FileSystems fileSystems)
|
||||
=> FileSystem = fileSystems.PartialViewsFileSystem ??
|
||||
throw new ArgumentException("Missing partial views file system", nameof(fileSystems));
|
||||
|
||||
protected override IFileSystem FileSystem { get; }
|
||||
|
||||
protected override string FileIcon(string path) => Constants.Icons.PartialView;
|
||||
|
||||
protected override string ItemType(string path) => Constants.UdiEntityType.PartialView;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree;
|
||||
|
||||
public class RootPartialViewTreeController : PartialViewTreeControllerBase
|
||||
{
|
||||
public RootPartialViewTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FileSystemTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Profiling;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/profiling")]
|
||||
[OpenApiTag("Profiling")]
|
||||
public class ProfilingControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Profiling;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Profiling;
|
||||
|
||||
public class StatusProfilingController : ProfilingControllerBase
|
||||
{
|
||||
private readonly IHostingEnvironment _hosting;
|
||||
|
||||
public StatusProfilingController(IHostingEnvironment hosting) => _hosting = hosting;
|
||||
|
||||
[HttpGet("status")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(ProfilingStatusViewModel), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<ProfilingStatusViewModel>> Status()
|
||||
=> await Task.FromResult(Ok(new ProfilingStatusViewModel(_hosting.IsDebugMode)));
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache;
|
||||
|
||||
public class CollectPublishedCacheController : PublishedCacheControllerBase
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
|
||||
public CollectPublishedCacheController(IPublishedSnapshotService publishedSnapshotService)
|
||||
=> _publishedSnapshotService = publishedSnapshotService;
|
||||
|
||||
[HttpPost("collect")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> Collect()
|
||||
{
|
||||
GC.Collect();
|
||||
await _publishedSnapshotService.CollectAsync();
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/published-cache")]
|
||||
[OpenApiTag("PublishedCache")]
|
||||
public class PublishedCacheControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache;
|
||||
|
||||
public class RebuildPublishedCacheController : PublishedCacheControllerBase
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
|
||||
public RebuildPublishedCacheController(IPublishedSnapshotService publishedSnapshotService)
|
||||
=> _publishedSnapshotService = publishedSnapshotService;
|
||||
|
||||
[HttpPost("rebuild")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> Collect()
|
||||
{
|
||||
_publishedSnapshotService.Rebuild();
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache;
|
||||
|
||||
public class ReloadPublishedCacheController : PublishedCacheControllerBase
|
||||
{
|
||||
private readonly DistributedCache _distributedCache;
|
||||
|
||||
public ReloadPublishedCacheController(DistributedCache distributedCache) => _distributedCache = distributedCache;
|
||||
|
||||
[HttpPost("reload")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult> Reload()
|
||||
{
|
||||
_distributedCache.RefreshAllPublishedSnapshot();
|
||||
return await Task.FromResult(Ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache;
|
||||
|
||||
public class StatusPublishedCacheController : PublishedCacheControllerBase
|
||||
{
|
||||
private readonly IPublishedSnapshotStatus _publishedSnapshotStatus;
|
||||
|
||||
public StatusPublishedCacheController(IPublishedSnapshotStatus publishedSnapshotStatus)
|
||||
=> _publishedSnapshotStatus = publishedSnapshotStatus;
|
||||
|
||||
[HttpGet("status")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<string>> Status()
|
||||
=> await Task.FromResult(Ok(_publishedSnapshotStatus.GetStatus()));
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Paging;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.RecycleBin;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.RecycleBin;
|
||||
|
||||
public abstract class RecycleBinControllerBase<TItem> : Controller
|
||||
where TItem : RecycleBinItemViewModel, new()
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly string _itemUdiType;
|
||||
|
||||
protected RecycleBinControllerBase(IEntityService entityService)
|
||||
{
|
||||
_entityService = entityService;
|
||||
// ReSharper disable once VirtualMemberCallInConstructor
|
||||
_itemUdiType = ItemObjectType.GetUdiType();
|
||||
}
|
||||
|
||||
protected abstract UmbracoObjectTypes ItemObjectType { get; }
|
||||
|
||||
protected abstract int RecycleBinRootId { get; }
|
||||
|
||||
protected async Task<ActionResult<PagedViewModel<TItem>>> GetRoot(int skip, int take)
|
||||
{
|
||||
if (PaginationService.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize, out ProblemDetails? error) == false)
|
||||
{
|
||||
return BadRequest(error);
|
||||
}
|
||||
|
||||
IEntitySlim[] rootEntities = GetPagedRootEntities(pageNumber, pageSize, out var totalItems);
|
||||
|
||||
TItem[] treeItemViewModels = MapRecycleBinViewModels(null, rootEntities);
|
||||
|
||||
PagedViewModel<TItem> result = PagedViewModel(treeItemViewModels, totalItems);
|
||||
return await Task.FromResult(Ok(result));
|
||||
}
|
||||
|
||||
protected async Task<ActionResult<PagedViewModel<TItem>>> GetChildren(Guid parentKey, int skip, int take)
|
||||
{
|
||||
if (PaginationService.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize, out ProblemDetails? error) == false)
|
||||
{
|
||||
return BadRequest(error);
|
||||
}
|
||||
|
||||
IEntitySlim[] children = GetPagedChildEntities(parentKey, pageNumber, pageSize, out var totalItems);
|
||||
|
||||
TItem[] treeItemViewModels = MapRecycleBinViewModels(parentKey, children);
|
||||
|
||||
PagedViewModel<TItem> result = PagedViewModel(treeItemViewModels, totalItems);
|
||||
|
||||
return await Task.FromResult(Ok(result));
|
||||
}
|
||||
|
||||
protected virtual TItem MapRecycleBinViewModel(Guid? parentKey, IEntitySlim entity)
|
||||
{
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
var viewModel = new TItem
|
||||
{
|
||||
Icon = _itemUdiType,
|
||||
Name = entity.Name!,
|
||||
Key = entity.Key,
|
||||
Type = _itemUdiType,
|
||||
HasChildren = entity.HasChildren,
|
||||
IsContainer = entity.IsContainer,
|
||||
ParentKey = parentKey
|
||||
};
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
private IEntitySlim[] GetPagedRootEntities(long pageNumber, int pageSize, out long totalItems)
|
||||
{
|
||||
IEntitySlim[] rootEntities = _entityService
|
||||
.GetPagedTrashedChildren(RecycleBinRootId, ItemObjectType, pageNumber, pageSize, out totalItems)
|
||||
.ToArray();
|
||||
|
||||
return rootEntities;
|
||||
}
|
||||
|
||||
private IEntitySlim[] GetPagedChildEntities(Guid parentKey, long pageNumber, int pageSize, out long totalItems)
|
||||
{
|
||||
IEntitySlim? parent = _entityService.Get(parentKey, ItemObjectType);
|
||||
if (parent == null || parent.Trashed == false)
|
||||
{
|
||||
// not much else we can do here but return nothing
|
||||
totalItems = 0;
|
||||
return Array.Empty<IEntitySlim>();
|
||||
}
|
||||
|
||||
IEntitySlim[] children = _entityService
|
||||
.GetPagedTrashedChildren(parent.Id, ItemObjectType, pageNumber, pageSize, out totalItems)
|
||||
.ToArray();
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
private TItem[] MapRecycleBinViewModels(Guid? parentKey, IEntitySlim[] entities)
|
||||
=> entities.Select(entity => MapRecycleBinViewModel(parentKey, entity)).ToArray();
|
||||
|
||||
private PagedViewModel<TItem> PagedViewModel(IEnumerable<TItem> treeItemViewModels, long totalItems)
|
||||
=> new() { Total = totalItems, Items = treeItemViewModels };
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Relation;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Relation;
|
||||
|
||||
public class ByChildRelationController : RelationControllerBase
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IRelationViewModelFactory _relationViewModelFactory;
|
||||
|
||||
public ByChildRelationController(
|
||||
IRelationService relationService,
|
||||
IRelationViewModelFactory relationViewModelFactory)
|
||||
{
|
||||
_relationService = relationService;
|
||||
_relationViewModelFactory = relationViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("childRelations/{childId:int}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<RelationViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<PagedViewModel<RelationViewModel>> ByChild(int childId, int skip, int take, string? relationTypeAlias = "")
|
||||
{
|
||||
IRelation[] relations = _relationService.GetByChildId(childId).ToArray();
|
||||
RelationViewModel[] result = Array.Empty<RelationViewModel>();
|
||||
|
||||
if (relations.Any())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(relationTypeAlias) == false)
|
||||
{
|
||||
result = _relationViewModelFactory.CreateMultiple(relations.Where(x =>
|
||||
x.RelationType.Alias.InvariantEquals(relationTypeAlias))).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _relationViewModelFactory.CreateMultiple(relations).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.FromResult(new PagedViewModel<RelationViewModel>
|
||||
{
|
||||
Total = result.Length,
|
||||
Items = result.Skip(skip).Take(take),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Factories;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Relation;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Relation;
|
||||
|
||||
public class ByIdRelationController : RelationControllerBase
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IRelationViewModelFactory _relationViewModelFactory;
|
||||
|
||||
public ByIdRelationController(IRelationService relationService, IRelationViewModelFactory relationViewModelFactory)
|
||||
{
|
||||
_relationService = relationService;
|
||||
_relationViewModelFactory = relationViewModelFactory;
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(RelationViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> ById(int id)
|
||||
{
|
||||
IRelation? relation = _relationService.GetById(id);
|
||||
if (relation is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await Task.FromResult(Ok(_relationViewModelFactory.Create(relation)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Relation;
|
||||
|
||||
[ApiController]
|
||||
[BackOfficeRoute("api/v{version:apiVersion}/relation")]
|
||||
[OpenApiTag("Relation")]
|
||||
[ApiVersion("1.0")]
|
||||
// TODO: Implement Authentication
|
||||
public abstract class RelationControllerBase : ManagementApiControllerBase
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree;
|
||||
|
||||
public class ItemsRelationTypeTreeController : RelationTypeTreeControllerBase
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
|
||||
public ItemsRelationTypeTreeController(IEntityService entityService, IRelationService relationService)
|
||||
: base(entityService) =>
|
||||
_relationService = relationService;
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FolderTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FolderTreeItemViewModel>>> Items([FromQuery(Name = "key")] Guid[] keys)
|
||||
{
|
||||
// relation service does not allow fetching a collection of relation types by their ids; instead it relies
|
||||
// heavily on caching, which means this is as fast as it gets - even if it looks less than performant
|
||||
IRelationType[] relationTypes = _relationService
|
||||
.GetAllRelationTypes()
|
||||
.Where(relationType => keys.Contains(relationType.Key)).ToArray();
|
||||
|
||||
EntityTreeItemViewModel[] viewModels = MapTreeItemViewModels(null, relationTypes);
|
||||
|
||||
return await Task.FromResult(Ok(viewModels));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.RelationType}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.RelationType))]
|
||||
// NOTE: at the moment relation types aren't supported by EntityService, so we have little use of the
|
||||
// tree controller base. We'll keep it though, in the hope that we can mend EntityService.
|
||||
public class RelationTypeTreeControllerBase : EntityTreeControllerBase<EntityTreeItemViewModel>
|
||||
{
|
||||
public RelationTypeTreeControllerBase(IEntityService entityService)
|
||||
: base(entityService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes ItemObjectType => UmbracoObjectTypes.RelationType;
|
||||
|
||||
protected EntityTreeItemViewModel[] MapTreeItemViewModels(Guid? parentKey, IRelationType[] relationTypes)
|
||||
=> relationTypes.Select(relationType => new EntityTreeItemViewModel
|
||||
{
|
||||
Icon = Constants.Icons.RelationType,
|
||||
Name = relationType.Name!,
|
||||
Key = relationType.Key,
|
||||
Type = Constants.UdiEntityType.RelationType,
|
||||
HasChildren = false,
|
||||
IsContainer = false,
|
||||
ParentKey = parentKey
|
||||
}).ToArray();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.ManagementApi.Services.Paging;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree;
|
||||
|
||||
public class RootRelationTypeTreeController : RelationTypeTreeControllerBase
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
|
||||
public RootRelationTypeTreeController(IEntityService entityService, IRelationService relationService)
|
||||
: base(entityService) =>
|
||||
_relationService = relationService;
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<EntityTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<EntityTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
{
|
||||
if (PaginationService.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize, out ProblemDetails? error) == false)
|
||||
{
|
||||
return BadRequest(error);
|
||||
}
|
||||
|
||||
// pagination is not supported (yet) by relation service, so we do it in memory for now
|
||||
// - chances are we won't have many relation types, so it won't be an actual issue
|
||||
IRelationType[] allRelationTypes = _relationService.GetAllRelationTypes().ToArray();
|
||||
|
||||
EntityTreeItemViewModel[] viewModels = MapTreeItemViewModels(
|
||||
null,
|
||||
allRelationTypes
|
||||
.OrderBy(relationType => relationType.Name)
|
||||
.Skip((int)(pageNumber * pageSize))
|
||||
.Take(pageSize)
|
||||
.ToArray());
|
||||
|
||||
PagedViewModel<EntityTreeItemViewModel> result = PagedViewModel(viewModels, allRelationTypes.Length);
|
||||
return await Task.FromResult(Ok(result));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree;
|
||||
|
||||
public class ChildrenScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
public ChildrenScriptTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("children")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FileSystemTreeItemViewModel>>> Children(string path, int skip = 0, int take = 100)
|
||||
=> await GetChildren(path, skip, take);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree;
|
||||
|
||||
public class ItemsScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
public ItemsScriptTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("items")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(IEnumerable<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<FileSystemTreeItemViewModel>>> Items([FromQuery(Name = "path")] string[] paths)
|
||||
=> await GetItems(paths);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Pagination;
|
||||
using Umbraco.Cms.ManagementApi.ViewModels.Tree;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree;
|
||||
|
||||
public class RootScriptTreeController : ScriptTreeControllerBase
|
||||
{
|
||||
public RootScriptTreeController(FileSystems fileSystems)
|
||||
: base(fileSystems)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet("root")]
|
||||
[MapToApiVersion("1.0")]
|
||||
[ProducesResponseType(typeof(PagedViewModel<FileSystemTreeItemViewModel>), StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<PagedViewModel<FileSystemTreeItemViewModel>>> Root(int skip = 0, int take = 100)
|
||||
=> await GetRoot(skip, take);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NSwag.Annotations;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.ManagementApi.Controllers.Tree;
|
||||
using Umbraco.New.Cms.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree;
|
||||
|
||||
[ApiVersion("1.0")]
|
||||
[ApiController]
|
||||
[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Script}/tree")]
|
||||
[OpenApiTag(nameof(Constants.UdiEntityType.Script))]
|
||||
public class ScriptTreeControllerBase : FileSystemTreeControllerBase
|
||||
{
|
||||
public ScriptTreeControllerBase(FileSystems fileSystems)
|
||||
=> FileSystem = fileSystems.ScriptsFileSystem ??
|
||||
throw new ArgumentException("Missing scripts file system", nameof(fileSystems));
|
||||
|
||||
protected override IFileSystem FileSystem { get; }
|
||||
|
||||
protected override string FileIcon(string path) => Constants.Icons.Script;
|
||||
|
||||
protected override string ItemType(string path) => Constants.UdiEntityType.Script;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user