Files
Umbraco-CMS/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs

321 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Umbraco.Core;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.ContentEditing;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Authorization;
using Umbraco.Extensions;
using Umbraco.Web.Common.ActionsResults;
using Umbraco.Web.Common.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
/// <inheritdoc />
/// <summary>
/// The API controller used for editing dictionary items
/// </summary>
/// <remarks>
/// The security for this controller is defined to allow full CRUD access to dictionary if the user has access to either:
/// Dictionary
/// </remarks>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDictionary)]
public class DictionaryController : BackOfficeNotificationsController
{
private readonly ILogger<DictionaryController> _logger;
private readonly ILocalizationService _localizationService;
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
private readonly GlobalSettings _globalSettings;
private readonly ILocalizedTextService _localizedTextService;
private readonly UmbracoMapper _umbracoMapper;
public DictionaryController(
ILogger<DictionaryController> logger,
ILocalizationService localizationService,
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
IOptions<GlobalSettings> globalSettings,
ILocalizedTextService localizedTextService,
UmbracoMapper umbracoMapper
)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
_backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor));
_globalSettings = globalSettings.Value ?? throw new ArgumentNullException(nameof(globalSettings));
_localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
_umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
}
/// <summary>
/// Deletes a data type with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns><see cref="HttpResponseMessage"/></returns>
[HttpDelete]
[HttpPost]
public IActionResult DeleteById(int id)
{
var foundDictionary = _localizationService.GetDictionaryItemById(id);
if (foundDictionary == null)
return NotFound();
var foundDictionaryDescendants = _localizationService.GetDictionaryItemDescendants(foundDictionary.Key);
foreach (var dictionaryItem in foundDictionaryDescendants)
{
_localizationService.Delete(dictionaryItem, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
}
_localizationService.Delete(foundDictionary, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
return Ok();
}
/// <summary>
/// Creates a new dictionary item
/// </summary>
/// <param name="parentId">
/// The parent id.
/// </param>
/// <param name="key">
/// The key.
/// </param>
/// <returns>
/// The <see cref="HttpResponseMessage"/>.
/// </returns>
[HttpPost]
public ActionResult<int> Create(int parentId, string key)
{
if (string.IsNullOrEmpty(key))
return ValidationErrorResult.CreateNotificationValidationErrorResult("Key can not be empty."); // TODO: translate
if (_localizationService.DictionaryItemExists(key))
{
var message = _localizedTextService.Localize(
"dictionaryItem/changeKeyError",
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings),
new Dictionary<string, string> { { "0", key } });
return ValidationErrorResult.CreateNotificationValidationErrorResult(message);
}
try
{
Guid? parentGuid = null;
if (parentId > 0)
parentGuid = _localizationService.GetDictionaryItemById(parentId).Key;
var item = _localizationService.CreateDictionaryItemWithIdentity(
key,
parentGuid,
string.Empty);
return item.Id;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating dictionary with {Name} under {ParentId}", key, parentId);
return ValidationErrorResult.CreateNotificationValidationErrorResult("Error creating dictionary item");
}
}
/// <summary>
/// Gets a dictionary item by id
/// </summary>
/// <param name="id">
/// The id.
/// </param>
/// <returns>
/// The <see cref="DictionaryDisplay"/>. Returns a not found response when dictionary item does not exist
/// </returns>
Merge remote-tracking branch 'origin/netcore/dev' into netcore/netcore # Conflicts: # build/NuSpecs/UmbracoCms.Web.nuspec # src/SolutionInfo.cs # src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs # src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs # src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts # src/Umbraco.Tests.AcceptanceTest/package.json # src/Umbraco.Tests/TestHelpers/TestObjects.cs # src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs # src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs # src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs # src/Umbraco.Web.BackOffice/Controllers/MediaController.cs # src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs # src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs # src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs # src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js # src/Umbraco.Web.UI.Client/src/installer/steps/database.html # src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less # src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less # src/Umbraco.Web.UI.Client/src/less/forms.less # src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html # src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html # src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html # src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-actions.html # src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js # src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html # src/Umbraco.Web.UI.Client/src/views/dictionary/edit.html # src/Umbraco.Web.UI.Client/src/views/dictionary/list.html # src/Umbraco.Web.UI.Client/src/views/logViewer/search.html # src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js # src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js # src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js # src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js # src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/layoutconfig.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts.prevalues.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.controller.js # src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml # src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Media.cshtml # src/Umbraco.Web.UI/config/umbracoSettings.config # src/Umbraco.Web/Editors/BackOfficeServerVariables.cs # src/Umbraco.Web/Editors/DictionaryController.cs # src/Umbraco.Web/Editors/LogController.cs # src/Umbraco.Web/Editors/MediaTypeController.cs # src/Umbraco.Web/Editors/MemberGroupController.cs
2020-09-09 19:13:37 +02:00
[DetermineAmbiguousActionByPassingParameters]
public ActionResult<DictionaryDisplay> GetById(int id)
{
var dictionary = _localizationService.GetDictionaryItemById(id);
if (dictionary == null)
return NotFound();
return _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionary);
}
/// <summary>
/// Gets a dictionary item by guid
/// </summary>
/// <param name="id">
/// The id.
/// </param>
/// <returns>
/// The <see cref="DictionaryDisplay"/>. Returns a not found response when dictionary item does not exist
/// </returns>
[DetermineAmbiguousActionByPassingParameters]
public ActionResult<DictionaryDisplay> GetById(Guid id)
{
var dictionary = _localizationService.GetDictionaryItemById(id);
if (dictionary == null)
return NotFound();
return _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionary);
}
/// <summary>
/// Gets a dictionary item by udi
/// </summary>
/// <param name="id">
/// The id.
/// </param>
/// <returns>
/// The <see cref="DictionaryDisplay"/>. Returns a not found response when dictionary item does not exist
/// </returns>
[DetermineAmbiguousActionByPassingParameters]
public ActionResult<DictionaryDisplay> GetById(Udi id)
{
var guidUdi = id as GuidUdi;
if (guidUdi == null)
return NotFound();
var dictionary = _localizationService.GetDictionaryItemById(guidUdi.Guid);
if (dictionary == null)
return NotFound();
return _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionary);
}
/// <summary>
/// Saves a dictionary item
/// </summary>
/// <param name="dictionary">
/// The dictionary.
/// </param>
/// <returns>
/// The <see cref="DictionaryDisplay"/>.
/// </returns>
public ActionResult<DictionaryDisplay> PostSave(DictionarySave dictionary)
{
var dictionaryItem =
_localizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString()));
if (dictionaryItem == null)
return ValidationErrorResult.CreateNotificationValidationErrorResult("Dictionary item does not exist");
var userCulture = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings);
if (dictionary.NameIsDirty)
{
// if the name (key) has changed, we need to check if the new key does not exist
var dictionaryByKey = _localizationService.GetDictionaryItemByKey(dictionary.Name);
if (dictionaryByKey != null && dictionaryItem.Id != dictionaryByKey.Id)
{
var message = _localizedTextService.Localize(
"dictionaryItem/changeKeyError",
userCulture,
new Dictionary<string, string> { { "0", dictionary.Name } });
ModelState.AddModelError("Name", message);
return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary()));
}
dictionaryItem.ItemKey = dictionary.Name;
}
foreach (var translation in dictionary.Translations)
{
_localizationService.AddOrUpdateDictionaryValue(dictionaryItem,
_localizationService.GetLanguageById(translation.LanguageId), translation.Translation);
}
try
{
_localizationService.Save(dictionaryItem);
var model = _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionaryItem);
2020-01-20 11:37:19 +01:00
model.Notifications.Add(new BackOfficeNotification(
_localizedTextService.Localize("speechBubbles/dictionaryItemSaved", userCulture), string.Empty,
NotificationStyle.Success));
return model;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error saving dictionary with {Name} under {ParentId}", dictionary.Name, dictionary.ParentId);
return ValidationErrorResult.CreateNotificationValidationErrorResult("Something went wrong saving dictionary");
}
}
/// <summary>
/// Retrieves a list with all dictionary items
/// </summary>
/// <returns>
/// The <see cref="IEnumerable{T}"/>.
/// </returns>
public IEnumerable<DictionaryOverviewDisplay> GetList()
{
var items = _localizationService.GetDictionaryItemDescendants(null).ToArray();
var list = new List<DictionaryOverviewDisplay>(items.Length);
// recursive method to build a tree structure from the flat structure returned above
void BuildTree(int level = 0, Guid? parentId = null)
{
var children = items.Where(t => t.ParentId == parentId).ToArray();
if(children.Any() == false)
{
return;
}
foreach(var child in children.OrderBy(ItemSort()))
{
var display = _umbracoMapper.Map<IDictionaryItem, DictionaryOverviewDisplay>(child);
display.Level = level;
list.Add(display);
BuildTree(level + 1, child.Key);
}
}
BuildTree();
return list;
}
/// <summary>
/// Get child items for list.
/// </summary>
/// <param name="dictionaryItem">
/// The dictionary item.
/// </param>
/// <param name="level">
/// The level.
/// </param>
/// <param name="list">
/// The list.
/// </param>
private void GetChildItemsForList(IDictionaryItem dictionaryItem, int level, ICollection<DictionaryOverviewDisplay> list)
{
foreach (var childItem in _localizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort()))
{
var item = _umbracoMapper.Map<IDictionaryItem, DictionaryOverviewDisplay>(childItem);
item.Level = level;
list.Add(item);
GetChildItemsForList(childItem, level + 1, list);
}
}
private static Func<IDictionaryItem, string> ItemSort() => item => item.ItemKey;
}
}