2020-12-22 16:36:07 +01:00
|
|
|
using System;
|
2018-06-13 15:17:31 +02:00
|
|
|
using System.Collections.Generic;
|
2021-09-14 22:13:39 +02:00
|
|
|
using System.Globalization;
|
2020-06-03 17:17:30 +02:00
|
|
|
using System.Linq;
|
2018-06-13 15:17:31 +02:00
|
|
|
using System.Net.Http;
|
2021-01-29 10:30:28 +01:00
|
|
|
using Microsoft.AspNetCore.Authorization;
|
2020-06-03 17:17:30 +02:00
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2020-09-21 09:27:54 +02:00
|
|
|
using Microsoft.Extensions.Logging;
|
2021-01-29 10:30:28 +01:00
|
|
|
using Microsoft.Extensions.Options;
|
2021-02-18 11:06:02 +01:00
|
|
|
using Umbraco.Cms.Core;
|
|
|
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Mapping;
|
|
|
|
|
using Umbraco.Cms.Core.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Models.ContentEditing;
|
|
|
|
|
using Umbraco.Cms.Core.Security;
|
|
|
|
|
using Umbraco.Cms.Core.Services;
|
|
|
|
|
using Umbraco.Cms.Web.Common.Attributes;
|
|
|
|
|
using Umbraco.Cms.Web.Common.Authorization;
|
2021-01-13 11:39:44 +01:00
|
|
|
using Umbraco.Extensions;
|
2021-02-18 11:06:02 +01:00
|
|
|
using Constants = Umbraco.Cms.Core.Constants;
|
2018-06-13 15:17:31 +02:00
|
|
|
|
2021-02-18 11:06:02 +01:00
|
|
|
namespace Umbraco.Cms.Web.BackOffice.Controllers
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2018-06-13 15:17:31 +02:00
|
|
|
/// <inheritdoc />
|
2018-06-13 20:19:18 +02:00
|
|
|
/// <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:
|
2019-01-26 10:52:19 -05:00
|
|
|
/// Dictionary
|
2018-06-13 20:19:18 +02:00
|
|
|
/// </remarks>
|
2020-06-09 13:01:05 +10:00
|
|
|
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
|
2020-11-19 19:23:41 +11:00
|
|
|
[Authorize(Policy = AuthorizationPolicies.TreeAccessDictionary)]
|
2021-01-29 10:30:28 +01:00
|
|
|
[ParameterSwapControllerActionSelector(nameof(GetById), "id", typeof(int), typeof(Guid), typeof(Udi))]
|
2018-06-13 20:19:18 +02:00
|
|
|
public class DictionaryController : BackOfficeNotificationsController
|
|
|
|
|
{
|
2020-09-21 09:27:54 +02:00
|
|
|
private readonly ILogger<DictionaryController> _logger;
|
2020-06-03 17:17:30 +02:00
|
|
|
private readonly ILocalizationService _localizationService;
|
2020-10-21 16:51:00 +11:00
|
|
|
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
2020-08-21 14:52:47 +01:00
|
|
|
private readonly GlobalSettings _globalSettings;
|
2020-06-03 17:17:30 +02:00
|
|
|
private readonly ILocalizedTextService _localizedTextService;
|
2021-04-20 19:34:18 +02:00
|
|
|
private readonly IUmbracoMapper _umbracoMapper;
|
2020-06-03 17:17:30 +02:00
|
|
|
|
2020-02-14 13:04:49 +01:00
|
|
|
public DictionaryController(
|
2020-09-21 09:27:54 +02:00
|
|
|
ILogger<DictionaryController> logger,
|
2020-06-03 17:17:30 +02:00
|
|
|
ILocalizationService localizationService,
|
2020-10-21 16:51:00 +11:00
|
|
|
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
2020-08-23 23:36:48 +02:00
|
|
|
IOptions<GlobalSettings> globalSettings,
|
2020-06-03 17:17:30 +02:00
|
|
|
ILocalizedTextService localizedTextService,
|
2021-04-20 19:34:18 +02:00
|
|
|
IUmbracoMapper umbracoMapper
|
2020-06-03 17:17:30 +02:00
|
|
|
)
|
2019-02-01 15:24:07 +11:00
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
|
|
|
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
|
2020-09-22 10:01:00 +02:00
|
|
|
_backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor));
|
2020-08-21 14:52:47 +01:00
|
|
|
_globalSettings = globalSettings.Value ?? throw new ArgumentNullException(nameof(globalSettings));
|
2020-06-03 17:17:30 +02:00
|
|
|
_localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
|
|
|
|
|
_umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper));
|
2019-02-01 15:24:07 +11:00
|
|
|
}
|
|
|
|
|
|
2018-06-13 20:19:18 +02:00
|
|
|
/// <summary>
|
2019-01-26 10:52:19 -05:00
|
|
|
/// Deletes a data type with a given ID
|
2018-06-13 20:19:18 +02:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id"></param>
|
|
|
|
|
/// <returns><see cref="HttpResponseMessage"/></returns>
|
|
|
|
|
[HttpDelete]
|
|
|
|
|
[HttpPost]
|
2020-06-03 17:17:30 +02:00
|
|
|
public IActionResult DeleteById(int id)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
var foundDictionary = _localizationService.GetDictionaryItemById(id);
|
2018-06-13 15:17:31 +02:00
|
|
|
|
2018-06-13 20:19:18 +02:00
|
|
|
if (foundDictionary == null)
|
2020-06-03 17:17:30 +02:00
|
|
|
return NotFound();
|
2019-02-14 12:40:45 +01:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
var foundDictionaryDescendants = _localizationService.GetDictionaryItemDescendants(foundDictionary.Key);
|
2019-06-01 13:07:16 +02:00
|
|
|
|
|
|
|
|
foreach (var dictionaryItem in foundDictionaryDescendants)
|
|
|
|
|
{
|
2020-10-21 16:51:00 +11:00
|
|
|
_localizationService.Delete(dictionaryItem, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
|
2019-06-01 13:07:16 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-21 16:51:00 +11:00
|
|
|
_localizationService.Delete(foundDictionary, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
return Ok();
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-01-26 10:52:19 -05:00
|
|
|
/// Creates a new dictionary item
|
2018-06-13 20:19:18 +02:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="parentId">
|
|
|
|
|
/// The parent id.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="key">
|
|
|
|
|
/// The key.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// The <see cref="HttpResponseMessage"/>.
|
|
|
|
|
/// </returns>
|
|
|
|
|
[HttpPost]
|
2020-06-03 17:17:30 +02:00
|
|
|
public ActionResult<int> Create(int parentId, string key)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(key))
|
2021-06-25 10:29:18 -06:00
|
|
|
return ValidationProblem("Key can not be empty."); // TODO: translate
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
if (_localizationService.DictionaryItemExists(key))
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
var message = _localizedTextService.Localize(
|
2021-07-05 20:58:04 +02:00
|
|
|
"dictionaryItem","changeKeyError",
|
2020-10-21 16:51:00 +11:00
|
|
|
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings),
|
2018-06-13 20:19:18 +02:00
|
|
|
new Dictionary<string, string> { { "0", key } });
|
2021-06-25 10:29:18 -06:00
|
|
|
return ValidationProblem(message);
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Guid? parentGuid = null;
|
|
|
|
|
|
|
|
|
|
if (parentId > 0)
|
2020-06-03 17:17:30 +02:00
|
|
|
parentGuid = _localizationService.GetDictionaryItemById(parentId).Key;
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
var item = _localizationService.CreateDictionaryItemWithIdentity(
|
2018-06-13 20:19:18 +02:00
|
|
|
key,
|
|
|
|
|
parentGuid,
|
|
|
|
|
string.Empty);
|
|
|
|
|
|
|
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
return item.Id;
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
2018-08-17 15:41:58 +01:00
|
|
|
catch (Exception ex)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-09-14 14:10:19 +02:00
|
|
|
_logger.LogError(ex, "Error creating dictionary with {Name} under {ParentId}", key, parentId);
|
2021-06-25 10:29:18 -06:00
|
|
|
return ValidationProblem("Error creating dictionary item");
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-26 08:05:15 +02:00
|
|
|
/// <summary>
|
2018-06-13 20:19:18 +02:00
|
|
|
/// Gets a dictionary item by id
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">
|
|
|
|
|
/// The id.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns>
|
2020-12-22 16:36:07 +01:00
|
|
|
/// The <see cref="DictionaryDisplay"/>. Returns a not found response when dictionary item does not exist
|
2018-06-13 20:19:18 +02:00
|
|
|
/// </returns>
|
2021-02-01 11:26:17 +01:00
|
|
|
public ActionResult<DictionaryDisplay> GetById(int id)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
var dictionary = _localizationService.GetDictionaryItemById(id);
|
2020-08-26 08:05:15 +02:00
|
|
|
if (dictionary == null)
|
|
|
|
|
return NotFound();
|
|
|
|
|
|
|
|
|
|
return _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionary);
|
|
|
|
|
}
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-08-26 08:05:15 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a dictionary item by guid
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">
|
|
|
|
|
/// The id.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns>
|
2020-12-22 16:36:07 +01:00
|
|
|
/// The <see cref="DictionaryDisplay"/>. Returns a not found response when dictionary item does not exist
|
2020-08-26 08:05:15 +02:00
|
|
|
/// </returns>
|
|
|
|
|
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>
|
2020-12-22 16:36:07 +01:00
|
|
|
/// The <see cref="DictionaryDisplay"/>. Returns a not found response when dictionary item does not exist
|
2020-08-26 08:05:15 +02:00
|
|
|
/// </returns>
|
|
|
|
|
public ActionResult<DictionaryDisplay> GetById(Udi id)
|
|
|
|
|
{
|
|
|
|
|
var guidUdi = id as GuidUdi;
|
|
|
|
|
if (guidUdi == null)
|
|
|
|
|
return NotFound();
|
|
|
|
|
|
|
|
|
|
var dictionary = _localizationService.GetDictionaryItemById(guidUdi.Guid);
|
2018-06-13 20:19:18 +02:00
|
|
|
if (dictionary == null)
|
2020-06-03 17:17:30 +02:00
|
|
|
return NotFound();
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
return _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionary);
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Saves a dictionary item
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="dictionary">
|
|
|
|
|
/// The dictionary.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// The <see cref="DictionaryDisplay"/>.
|
2019-02-14 12:40:45 +01:00
|
|
|
/// </returns>
|
2020-12-22 16:36:07 +01:00
|
|
|
public ActionResult<DictionaryDisplay> PostSave(DictionarySave dictionary)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
|
|
|
|
var dictionaryItem =
|
2021-09-14 22:13:39 +02:00
|
|
|
_localizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString(), CultureInfo.InvariantCulture));
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
if (dictionaryItem == null)
|
2021-06-25 10:29:18 -06:00
|
|
|
return ValidationProblem("Dictionary item does not exist");
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-10-21 16:51:00 +11:00
|
|
|
var userCulture = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
if (dictionary.NameIsDirty)
|
|
|
|
|
{
|
|
|
|
|
// if the name (key) has changed, we need to check if the new key does not exist
|
2020-06-03 17:17:30 +02:00
|
|
|
var dictionaryByKey = _localizationService.GetDictionaryItemByKey(dictionary.Name);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
if (dictionaryByKey != null && dictionaryItem.Id != dictionaryByKey.Id)
|
|
|
|
|
{
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
var message = _localizedTextService.Localize(
|
2021-07-05 20:58:04 +02:00
|
|
|
"dictionaryItem","changeKeyError",
|
2018-06-13 15:17:31 +02:00
|
|
|
userCulture,
|
|
|
|
|
new Dictionary<string, string> { { "0", dictionary.Name } });
|
|
|
|
|
ModelState.AddModelError("Name", message);
|
2021-06-25 10:29:18 -06:00
|
|
|
return ValidationProblem(ModelState);
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
dictionaryItem.ItemKey = dictionary.Name;
|
|
|
|
|
}
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
foreach (var translation in dictionary.Translations)
|
|
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
_localizationService.AddOrUpdateDictionaryValue(dictionaryItem,
|
|
|
|
|
_localizationService.GetLanguageById(translation.LanguageId), translation.Translation);
|
2018-06-13 15:17:31 +02:00
|
|
|
}
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
try
|
|
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
_localizationService.Save(dictionaryItem);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-06-03 17:17:30 +02:00
|
|
|
var model = _umbracoMapper.Map<IDictionaryItem, DictionaryDisplay>(dictionaryItem);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-01-20 11:37:19 +01:00
|
|
|
model.Notifications.Add(new BackOfficeNotification(
|
2021-07-05 20:58:04 +02:00
|
|
|
_localizedTextService.Localize("speechBubbles","dictionaryItemSaved", userCulture), string.Empty,
|
2019-01-29 23:05:59 +11:00
|
|
|
NotificationStyle.Success));
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
return model;
|
|
|
|
|
}
|
2018-08-17 15:41:58 +01:00
|
|
|
catch (Exception ex)
|
2018-06-13 15:17:31 +02:00
|
|
|
{
|
2020-09-14 14:10:19 +02:00
|
|
|
_logger.LogError(ex, "Error saving dictionary with {Name} under {ParentId}", dictionary.Name, dictionary.ParentId);
|
2021-06-25 10:29:18 -06:00
|
|
|
return ValidationProblem("Something went wrong saving dictionary");
|
2018-06-13 15:17:31 +02:00
|
|
|
}
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Retrieves a list with all dictionary items
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// The <see cref="IEnumerable{T}"/>.
|
|
|
|
|
/// </returns>
|
|
|
|
|
public IEnumerable<DictionaryOverviewDisplay> GetList()
|
|
|
|
|
{
|
2020-08-26 08:05:15 +02:00
|
|
|
var items = _localizationService.GetDictionaryItemDescendants(null).ToArray();
|
|
|
|
|
var list = new List<DictionaryOverviewDisplay>(items.Length);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-08-26 08:05:15 +02:00
|
|
|
// recursive method to build a tree structure from the flat structure returned above
|
|
|
|
|
void BuildTree(int level = 0, Guid? parentId = null)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-08-26 08:05:15 +02:00
|
|
|
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);
|
2018-06-13 20:19:18 +02:00
|
|
|
|
2020-08-26 08:05:15 +02:00
|
|
|
BuildTree(level + 1, child.Key);
|
|
|
|
|
}
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-26 08:05:15 +02:00
|
|
|
BuildTree();
|
|
|
|
|
|
2018-06-13 20:19:18 +02:00
|
|
|
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>
|
2019-01-17 00:29:43 +11:00
|
|
|
private void GetChildItemsForList(IDictionaryItem dictionaryItem, int level, ICollection<DictionaryOverviewDisplay> list)
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
foreach (var childItem in _localizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort()))
|
2018-06-13 20:19:18 +02:00
|
|
|
{
|
2020-06-03 17:17:30 +02:00
|
|
|
var item = _umbracoMapper.Map<IDictionaryItem, DictionaryOverviewDisplay>(childItem);
|
2018-06-13 20:19:18 +02:00
|
|
|
item.Level = level;
|
|
|
|
|
list.Add(item);
|
|
|
|
|
|
2018-06-13 15:17:31 +02:00
|
|
|
GetChildItemsForList(childItem, level + 1, list);
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-11-06 15:56:30 +01:00
|
|
|
|
2019-01-17 00:29:43 +11:00
|
|
|
private static Func<IDictionaryItem, string> ItemSort() => item => item.ItemKey;
|
2018-06-13 20:19:18 +02:00
|
|
|
}
|
|
|
|
|
}
|