Fixes Dictionary packaging to ensure an xml hierarchy

This commit is contained in:
Shannon
2021-06-16 13:14:28 -06:00
parent 095576352b
commit 9f02b14a5d
4 changed files with 179 additions and 32 deletions

View File

@@ -71,7 +71,8 @@ namespace Umbraco.Cms.Core.Packaging
string packagesFolderPath = null,
string mediaFolderPath = null)
{
if (string.IsNullOrWhiteSpace(packageRepositoryFileName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageRepositoryFileName));
if (string.IsNullOrWhiteSpace(packageRepositoryFileName))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageRepositoryFileName));
_contentService = contentService;
_contentTypeService = contentTypeService;
_dataTypeService = dataTypeService;
@@ -114,7 +115,8 @@ namespace Umbraco.Cms.Core.Packaging
{
var packagesXml = EnsureStorage(out var packagesFile);
var packageXml = packagesXml?.Root?.Elements("package").FirstOrDefault(x => x.AttributeValue<int>("id") == id);
if (packageXml == null) return;
if (packageXml == null)
return;
packageXml.Remove();
@@ -123,7 +125,8 @@ namespace Umbraco.Cms.Core.Packaging
public bool SavePackage(PackageDefinition definition)
{
if (definition == null) throw new ArgumentNullException(nameof(definition));
if (definition == null)
throw new ArgumentNullException(nameof(definition));
var packagesXml = EnsureStorage(out var packagesFile);
@@ -162,8 +165,10 @@ namespace Umbraco.Cms.Core.Packaging
public string ExportPackage(PackageDefinition definition)
{
if (definition.Id == default) throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported");
if (definition.PackageId == default) throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported");
if (definition.Id == default)
throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported");
if (definition.PackageId == default)
throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported");
//ensure it's valid
ValidatePackage(definition);
@@ -248,9 +253,11 @@ namespace Umbraco.Cms.Core.Packaging
var dataTypes = new XElement("DataTypes");
foreach (var dtId in definition.DataTypes)
{
if (!int.TryParse(dtId, out var outInt)) continue;
if (!int.TryParse(dtId, out var outInt))
continue;
var dataType = _dataTypeService.GetDataType(outInt);
if (dataType == null) continue;
if (dataType == null)
continue;
dataTypes.Add(_serializer.Serialize(dataType));
}
root.Add(dataTypes);
@@ -261,9 +268,11 @@ namespace Umbraco.Cms.Core.Packaging
var languages = new XElement("Languages");
foreach (var langId in definition.Languages)
{
if (!int.TryParse(langId, out var outInt)) continue;
if (!int.TryParse(langId, out var outInt))
continue;
var lang = _languageService.GetLanguageById(outInt);
if (lang == null) continue;
if (lang == null)
continue;
languages.Add(_serializer.Serialize(lang));
}
root.Add(languages);
@@ -271,15 +280,74 @@ namespace Umbraco.Cms.Core.Packaging
private void PackageDictionaryItems(PackageDefinition definition, XContainer root)
{
var dictionaryItems = new XElement("DictionaryItems");
var rootDictionaryItems = new XElement("DictionaryItems");
var items = new Dictionary<Guid, (IDictionaryItem dictionaryItem, XElement serializedDictionaryValue)>();
foreach (var dictionaryId in definition.DictionaryItems)
{
if (!int.TryParse(dictionaryId, out var outInt)) continue;
var di = _languageService.GetDictionaryItemById(outInt);
if (di == null) continue;
dictionaryItems.Add(_serializer.Serialize(di, false));
if (!int.TryParse(dictionaryId, out var outInt))
{
continue;
}
IDictionaryItem di = _languageService.GetDictionaryItemById(outInt);
if (di == null)
{
continue;
}
items[di.Key] = (di, _serializer.Serialize(di, false));
}
// organize them in hierarchy ...
var itemCount = items.Count;
var processed = new Dictionary<Guid, XElement>();
while (processed.Count < itemCount)
{
foreach(Guid key in items.Keys.ToList())
{
(IDictionaryItem dictionaryItem, XElement serializedDictionaryValue) = items[key];
if (!dictionaryItem.ParentId.HasValue)
{
// if it has no parent, its definitely just at the root
AppendDictionaryElement(rootDictionaryItems, items, processed, key, serializedDictionaryValue);
}
else
{
if (processed.ContainsKey(dictionaryItem.ParentId.Value))
{
// we've processed this parent element already so we can just append this xml child to it
AppendDictionaryElement(processed[dictionaryItem.ParentId.Value], items, processed, key, serializedDictionaryValue);
}
else if (items.ContainsKey(dictionaryItem.ParentId.Value))
{
// we know the parent exists in the dictionary but
// we haven't processed it yet so we'll leave it for the next loop
continue;
}
else
{
// in this case, the parent of this item doesn't exist in our collection, we have no
// choice but to add it to the root.
AppendDictionaryElement(rootDictionaryItems, items, processed, key, serializedDictionaryValue);
}
}
}
}
root.Add(rootDictionaryItems);
static void AppendDictionaryElement(XElement rootDictionaryItems, Dictionary<Guid, (IDictionaryItem dictionaryItem, XElement serializedDictionaryValue)> items, Dictionary<Guid, XElement> processed, Guid key, XElement serializedDictionaryValue)
{
// track it
processed.Add(key, serializedDictionaryValue);
// append it
rootDictionaryItems.Add(serializedDictionaryValue);
// remove it so its not re-processed
items.Remove(key);
}
root.Add(dictionaryItems);
}
private void PackageMacros(PackageDefinition definition, XContainer root)
@@ -287,11 +355,13 @@ namespace Umbraco.Cms.Core.Packaging
var macros = new XElement("Macros");
foreach (var macroId in definition.Macros)
{
if (!int.TryParse(macroId, out var outInt)) continue;
if (!int.TryParse(macroId, out var outInt))
continue;
var macroXml = GetMacroXml(outInt, out var macro);
if (macroXml == null) continue;
macros.Add(macroXml);
if (macroXml == null)
continue;
macros.Add(macroXml);
}
root.Add(macros);
}
@@ -301,7 +371,8 @@ namespace Umbraco.Cms.Core.Packaging
var stylesheetsXml = new XElement("Stylesheets");
foreach (var stylesheetName in definition.Stylesheets)
{
if (stylesheetName.IsNullOrWhiteSpace()) continue;
if (stylesheetName.IsNullOrWhiteSpace())
continue;
var xml = GetStylesheetXml(stylesheetName, true);
if (xml != null)
stylesheetsXml.Add(xml);
@@ -314,9 +385,11 @@ namespace Umbraco.Cms.Core.Packaging
var templatesXml = new XElement("Templates");
foreach (var templateId in definition.Templates)
{
if (!int.TryParse(templateId, out var outInt)) continue;
if (!int.TryParse(templateId, out var outInt))
continue;
var template = _fileService.GetTemplate(outInt);
if (template == null) continue;
if (template == null)
continue;
templatesXml.Add(_serializer.Serialize(template));
}
root.Add(templatesXml);
@@ -328,9 +401,11 @@ namespace Umbraco.Cms.Core.Packaging
var docTypesXml = new XElement("DocumentTypes");
foreach (var dtId in definition.DocumentTypes)
{
if (!int.TryParse(dtId, out var outInt)) continue;
if (!int.TryParse(dtId, out var outInt))
continue;
var contentType = _contentTypeService.Get(outInt);
if (contentType == null) continue;
if (contentType == null)
continue;
AddDocumentType(contentType, contentTypes);
}
foreach (var contentType in contentTypes)
@@ -345,9 +420,11 @@ namespace Umbraco.Cms.Core.Packaging
var mediaTypesXml = new XElement("MediaTypes");
foreach (var mediaTypeId in definition.MediaTypes)
{
if (!int.TryParse(mediaTypeId, out var outInt)) continue;
if (!int.TryParse(mediaTypeId, out var outInt))
continue;
var mediaType = _mediaTypeService.Get(outInt);
if (mediaType == null) continue;
if (mediaType == null)
continue;
AddMediaType(mediaType, mediaTypes);
}
foreach (var mediaType in mediaTypes)
@@ -456,7 +533,8 @@ namespace Umbraco.Cms.Core.Packaging
private XElement GetMacroXml(int macroId, out IMacro macro)
{
macro = _macroService.GetById(macroId);
if (macro == null) return null;
if (macro == null)
return null;
var xml = _serializer.Serialize(macro);
return xml;
}
@@ -469,15 +547,18 @@ namespace Umbraco.Cms.Core.Packaging
/// <returns></returns>
private XElement GetStylesheetXml(string name, bool includeProperties)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
var sts = _fileService.GetStylesheetByName(name);
if (sts == null) return null;
if (sts == null)
return null;
var stylesheetXml = new XElement("Stylesheet");
stylesheetXml.Add(new XElement("Name", sts.Alias));
stylesheetXml.Add(new XElement("FileName", sts.Name));
stylesheetXml.Add(new XElement("Content", new XCData(sts.Content)));
if (!includeProperties) return stylesheetXml;
if (!includeProperties)
return stylesheetXml;
var properties = new XElement("Properties");
foreach (var ssP in sts.Properties)

View File

@@ -1127,8 +1127,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging
IDictionaryItem dictionaryItem;
var itemName = dictionaryItemElement.Attribute("Name").Value;
var key = Guid.Parse(dictionaryItemElement.Attribute("Key").Value);
Guid key = dictionaryItemElement.RequiredAttributeValue<Guid>("Key");
dictionaryItem = _localizationService.GetDictionaryItemById(key);
if (dictionaryItem != null)
{

View File

@@ -152,6 +152,72 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging
// TODO: There's a whole lot more assertions to be done
}
[Test]
public void GivenNestedDictionaryItems_WhenPackageExported_ThenTheXmlIsNested()
{
var parent = new DictionaryItem("Parent")
{
Key = Guid.NewGuid()
};
LocalizationService.Save(parent);
var child1 = new DictionaryItem(parent.Key, "Child1")
{
Key = Guid.NewGuid()
};
LocalizationService.Save(child1);
var child2 = new DictionaryItem(child1.Key, "Child2")
{
Key = Guid.NewGuid()
};
LocalizationService.Save(child2);
var child3 = new DictionaryItem(child2.Key, "Child3")
{
Key = Guid.NewGuid()
};
LocalizationService.Save(child3);
var child4 = new DictionaryItem(child3.Key, "Child4")
{
Key = Guid.NewGuid()
};
LocalizationService.Save(child4);
var def = new PackageDefinition
{
Name = "test",
// put these out of order to ensure that it doesn't matter.
DictionaryItems = new List<string>
{
child2.Id.ToString(),
child1.Id.ToString(),
// we are missing 3 here so 4 will be orphaned and end up in the root
child4.Id.ToString(),
parent.Id.ToString()
}
};
PackageBuilder.SavePackage(def);
string packageXmlPath = PackageBuilder.ExportPackage(def);
using (var stream = File.OpenRead(packageXmlPath))
{
var xml = XDocument.Load(stream);
var dictionaryItems = xml.Root.Element("DictionaryItems");
Assert.IsNotNull(dictionaryItems);
var rootItems = dictionaryItems.Elements("DictionaryItem").ToList();
Assert.AreEqual(2, rootItems.Count);
Assert.AreEqual("Child4", rootItems[0].AttributeValue<string>("Name"));
Assert.AreEqual("Parent", rootItems[1].AttributeValue<string>("Name"));
var children = rootItems[1].Elements("DictionaryItem").ToList();
Assert.AreEqual(1, children.Count);
Assert.AreEqual("Child1", children[0].AttributeValue<string>("Name"));
children = children[0].Elements("DictionaryItem").ToList();
Assert.AreEqual(1, children.Count);
Assert.AreEqual("Child2", children[0].AttributeValue<string>("Name"));
}
}
[Test]
public void Export()
{

View File

@@ -1,4 +1,4 @@
// Copyright (c) Umbraco.
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;