fixes U4-5140 Package installation for content does not respect tags, however need to check with Morten if it's required because if we never actually publish content on package install then this wouldn't really matter. But surely we must publish on package install for some thing because starter kits are published... hrm.
This commit is contained in:
@@ -1,38 +1,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class TaggedEntity
|
||||
{
|
||||
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
|
||||
{
|
||||
EntityId = entityId;
|
||||
TaggedProperties = taggedProperties;
|
||||
}
|
||||
|
||||
public int EntityId { get; private set; }
|
||||
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
|
||||
}
|
||||
|
||||
public class TaggedProperty
|
||||
{
|
||||
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<Tag> tags)
|
||||
{
|
||||
PropertyTypeId = propertyTypeId;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
public int PropertyTypeId { get; private set; }
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
public IEnumerable<Tag> Tags { get; private set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Tag : Entity, ITag
|
||||
|
||||
16
src/Umbraco.Core/Models/TaggedEntity.cs
Normal file
16
src/Umbraco.Core/Models/TaggedEntity.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class TaggedEntity
|
||||
{
|
||||
public TaggedEntity(int entityId, IEnumerable<TaggedProperty> taggedProperties)
|
||||
{
|
||||
EntityId = entityId;
|
||||
TaggedProperties = taggedProperties;
|
||||
}
|
||||
|
||||
public int EntityId { get; private set; }
|
||||
public IEnumerable<TaggedProperty> TaggedProperties { get; private set; }
|
||||
}
|
||||
}
|
||||
18
src/Umbraco.Core/Models/TaggedProperty.cs
Normal file
18
src/Umbraco.Core/Models/TaggedProperty.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class TaggedProperty
|
||||
{
|
||||
public TaggedProperty(int propertyTypeId, string propertyTypeAlias, IEnumerable<ITag> tags)
|
||||
{
|
||||
PropertyTypeId = propertyTypeId;
|
||||
PropertyTypeAlias = propertyTypeAlias;
|
||||
Tags = tags;
|
||||
}
|
||||
|
||||
public int PropertyTypeId { get; private set; }
|
||||
public string PropertyTypeAlias { get; private set; }
|
||||
public IEnumerable<ITag> Tags { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -106,6 +108,17 @@ namespace Umbraco.Core.Services
|
||||
return Enumerable.Empty<IContent>();
|
||||
}
|
||||
|
||||
//check for tag properties element at root
|
||||
XElement tagProperties = null;
|
||||
if (element.Document != null && element.Document.Root != null)
|
||||
{
|
||||
var found = element.Document.Root.XPathSelectElement("/umbPackage/TagProperties");
|
||||
if (found != null)
|
||||
{
|
||||
tagProperties = found;
|
||||
}
|
||||
}
|
||||
|
||||
var name = element.Name.LocalName;
|
||||
if (name.Equals("DocumentSet"))
|
||||
{
|
||||
@@ -114,7 +127,7 @@ namespace Umbraco.Core.Services
|
||||
where (string)doc.Attribute("isDoc") == ""
|
||||
select doc;
|
||||
|
||||
var contents = ParseDocumentRootXml(roots, parentId);
|
||||
var contents = ParseDocumentRootXml(roots, parentId, tagProperties);
|
||||
if (contents.Any())
|
||||
_contentService.Save(contents, userId);
|
||||
|
||||
@@ -128,7 +141,7 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
//This is a single doc import
|
||||
var elements = new List<XElement> { element };
|
||||
var contents = ParseDocumentRootXml(elements, parentId);
|
||||
var contents = ParseDocumentRootXml(elements, parentId, tagProperties);
|
||||
if (contents.Any())
|
||||
_contentService.Save(contents, userId);
|
||||
|
||||
@@ -142,7 +155,7 @@ namespace Umbraco.Core.Services
|
||||
"'DocumentSet' (for structured imports) nor is the first element a Document (for single document import).");
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> ParseDocumentRootXml(IEnumerable<XElement> roots, int parentId)
|
||||
private IEnumerable<IContent> ParseDocumentRootXml(IEnumerable<XElement> roots, int parentId, XElement tagProperties)
|
||||
{
|
||||
var contents = new List<IContent>();
|
||||
foreach (var root in roots)
|
||||
@@ -158,19 +171,19 @@ namespace Umbraco.Core.Services
|
||||
_importedContentTypes.Add(contentTypeAlias, contentType);
|
||||
}
|
||||
|
||||
var content = CreateContentFromXml(root, _importedContentTypes[contentTypeAlias], null, parentId, isLegacySchema);
|
||||
var content = CreateContentFromXml(root, _importedContentTypes[contentTypeAlias], null, parentId, isLegacySchema, tagProperties);
|
||||
contents.Add(content);
|
||||
|
||||
var children = from child in root.Elements()
|
||||
where (string)child.Attribute("isDoc") == ""
|
||||
select child;
|
||||
if (children.Any())
|
||||
contents.AddRange(CreateContentFromXml(children, content, isLegacySchema));
|
||||
contents.AddRange(CreateContentFromXml(children, content, isLegacySchema, tagProperties));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> CreateContentFromXml(IEnumerable<XElement> children, IContent parent, bool isLegacySchema)
|
||||
private IEnumerable<IContent> CreateContentFromXml(IEnumerable<XElement> children, IContent parent, bool isLegacySchema, XElement tagProperties)
|
||||
{
|
||||
var list = new List<IContent>();
|
||||
foreach (var child in children)
|
||||
@@ -186,7 +199,7 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
|
||||
//Create and add the child to the list
|
||||
var content = CreateContentFromXml(child, _importedContentTypes[contentTypeAlias], parent, default(int), isLegacySchema);
|
||||
var content = CreateContentFromXml(child, _importedContentTypes[contentTypeAlias], parent, default(int), isLegacySchema, tagProperties);
|
||||
list.Add(content);
|
||||
|
||||
//Recursive call
|
||||
@@ -196,13 +209,13 @@ namespace Umbraco.Core.Services
|
||||
select grand;
|
||||
|
||||
if (grandChildren.Any())
|
||||
list.AddRange(CreateContentFromXml(grandChildren, content, isLegacySchema));
|
||||
list.AddRange(CreateContentFromXml(grandChildren, content, isLegacySchema, tagProperties));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId, bool isLegacySchema)
|
||||
private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId, bool isLegacySchema, XElement tagProperties)
|
||||
{
|
||||
var id = element.Attribute("id").Value;
|
||||
var level = element.Attribute("level").Value;
|
||||
@@ -235,21 +248,47 @@ namespace Umbraco.Core.Services
|
||||
var propertyValue = property.Value;
|
||||
|
||||
var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias);
|
||||
if (propertyType != null && propertyType.PropertyEditorAlias == Constants.PropertyEditors.CheckBoxListAlias)
|
||||
{
|
||||
var database = ApplicationContext.Current.DatabaseContext.Database;
|
||||
var dtos = database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = propertyType.DataTypeDefinitionId });
|
||||
|
||||
var propertyValueList = new List<string>();
|
||||
foreach (var preValue in propertyValue.Split(','))
|
||||
//TODO: It would be heaps nicer if we didn't have to hard code references to specific property editors
|
||||
// we'd have to modify the packaging format to denote how to parse/store the value instead of relying on this
|
||||
|
||||
if (propertyType != null)
|
||||
{
|
||||
if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.CheckBoxListAlias)
|
||||
{
|
||||
propertyValueList.Add(dtos.Single(x => x.Value == preValue).Id.ToString(CultureInfo.InvariantCulture));
|
||||
var database = ApplicationContext.Current.DatabaseContext.Database;
|
||||
var dtos = database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new {Id = propertyType.DataTypeDefinitionId});
|
||||
|
||||
var propertyValueList = new List<string>();
|
||||
foreach (var preValue in propertyValue.Split(','))
|
||||
{
|
||||
propertyValueList.Add(dtos.Single(x => x.Value == preValue).Id.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
propertyValue = string.Join(",", propertyValueList.ToArray());
|
||||
|
||||
//set value as per normal
|
||||
content.SetValue(propertyTypeAlias, propertyValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
//check if this exists in tagProperties
|
||||
var hasTags = tagProperties.XPathSelectElement(string.Format("//TagProperty[@docId=\"{0}\" and @propertyAlias=\"{1}\"]", id, propertyType.Alias));
|
||||
if (hasTags != null)
|
||||
{
|
||||
var tags = JsonConvert.DeserializeObject<string[]>(hasTags.Value);
|
||||
content.SetTags(propertyTypeAlias, tags, true, hasTags.Attribute("group").Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
propertyValue = string.Join(",", propertyValueList.ToArray());
|
||||
}
|
||||
|
||||
content.SetValue(propertyTypeAlias, propertyValue);
|
||||
else
|
||||
{
|
||||
//set value as per normal
|
||||
content.SetValue(propertyTypeAlias, propertyValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,6 +364,8 @@
|
||||
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
|
||||
<Compile Include="Models\TagCacheStorageType.cs" />
|
||||
<Compile Include="Models\TaggableObjectTypes.cs" />
|
||||
<Compile Include="Models\TaggedEntity.cs" />
|
||||
<Compile Include="Models\TaggedProperty.cs" />
|
||||
<Compile Include="Models\TemplateNode.cs" />
|
||||
<Compile Include="Models\UmbracoEntityExtensions.cs" />
|
||||
<Compile Include="Packaging\PackageBinaryByteInspector.cs" />
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using umbraco.cms.businesslogic.template;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using umbraco.cms.businesslogic.macro;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using File = System.IO.File;
|
||||
using Template = umbraco.cms.businesslogic.template.Template;
|
||||
|
||||
|
||||
namespace umbraco.cms.businesslogic.packager
|
||||
@@ -120,14 +126,14 @@ namespace umbraco.cms.businesslogic.packager
|
||||
//Info section..
|
||||
AppendElement(utill.PackageInfo(pack, _packageManifest));
|
||||
|
||||
//Documents...
|
||||
//Documents and tags...
|
||||
var contentNodeId = 0;
|
||||
if (string.IsNullOrEmpty(pack.ContentNodeId) == false && int.TryParse(pack.ContentNodeId, out contentNodeId))
|
||||
{
|
||||
if (contentNodeId > 0)
|
||||
{
|
||||
//Create the Documents/DocumentSet node
|
||||
XmlNode documents = _packageManifest.CreateElement("Documents");
|
||||
|
||||
XmlNode documentSet = _packageManifest.CreateElement("DocumentSet");
|
||||
XmlAttribute importMode = _packageManifest.CreateAttribute("importMode", "");
|
||||
importMode.Value = "root";
|
||||
@@ -139,7 +145,66 @@ namespace umbraco.cms.businesslogic.packager
|
||||
|
||||
documentSet.AppendChild(umbDocument.ToXml(_packageManifest, pack.ContentLoadChildNodes));
|
||||
|
||||
AppendElement(documents);
|
||||
AppendElement(documents);
|
||||
|
||||
//Create the TagProperties node - this is used to store a definition for all
|
||||
// document properties that are tags, this ensures that we can re-import tags properly
|
||||
XmlNode tagProps = _packageManifest.CreateElement("TagProperties");
|
||||
|
||||
//before we try to populate this, we'll do a quick lookup to see if any of the documents
|
||||
// being exported contain published tags.
|
||||
var allExportedIds = documents.SelectNodes("//@id").Cast<XmlNode>()
|
||||
.Select(x => x.Value.TryConvertTo<int>())
|
||||
.Where(x => x.Success)
|
||||
.Select(x => x.Result)
|
||||
.ToArray();
|
||||
var allContentTags = new List<ITag>();
|
||||
foreach (var exportedId in allExportedIds)
|
||||
{
|
||||
allContentTags.AddRange(
|
||||
ApplicationContext.Current.Services.TagService.GetTagsForEntity(exportedId));
|
||||
}
|
||||
|
||||
//This is pretty round-about but it works. Essentially we need to get the properties that are tagged
|
||||
// but to do that we need to lookup by a tag (string)
|
||||
var allTaggedEntities = new List<TaggedEntity>();
|
||||
foreach (var group in allContentTags.Select(x => x.Group).Distinct())
|
||||
{
|
||||
allTaggedEntities.AddRange(
|
||||
ApplicationContext.Current.Services.TagService.GetTaggedContentByTagGroup(group));
|
||||
}
|
||||
|
||||
//Now, we have all property Ids/Aliases and their referenced document Ids and tags
|
||||
var allExportedTaggedEntities = allTaggedEntities.Where(x => allExportedIds.Contains(x.EntityId))
|
||||
.DistinctBy(x => x.EntityId)
|
||||
.OrderBy(x => x.EntityId);
|
||||
|
||||
foreach (var taggedEntity in allExportedTaggedEntities)
|
||||
{
|
||||
foreach (var taggedProperty in taggedEntity.TaggedProperties.Where(x => x.Tags.Any()))
|
||||
{
|
||||
XmlNode tagProp = _packageManifest.CreateElement("TagProperty");
|
||||
var docId = _packageManifest.CreateAttribute("docId", "");
|
||||
docId.Value = taggedEntity.EntityId.ToString(CultureInfo.InvariantCulture);
|
||||
tagProp.Attributes.Append(docId);
|
||||
|
||||
var propertyAlias = _packageManifest.CreateAttribute("propertyAlias", "");
|
||||
propertyAlias.Value = taggedProperty.PropertyTypeAlias;
|
||||
tagProp.Attributes.Append(propertyAlias);
|
||||
|
||||
var group = _packageManifest.CreateAttribute("group", "");
|
||||
group.Value = taggedProperty.Tags.First().Group;
|
||||
tagProp.Attributes.Append(group);
|
||||
|
||||
tagProp.AppendChild(_packageManifest.CreateCDataSection(
|
||||
JsonConvert.SerializeObject(taggedProperty.Tags.Select(x => x.Text).ToArray())));
|
||||
|
||||
tagProps.AppendChild(tagProp);
|
||||
}
|
||||
}
|
||||
|
||||
AppendElement(tagProps);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<packages>
|
||||
<package id="ClientDependency" version="1.7.1.2" targetFramework="net40" />
|
||||
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="6.0.2" targetFramework="net45" />
|
||||
<package id="SharpZipLib" version="0.86.0" targetFramework="net40" />
|
||||
<package id="Tidy.Net" version="1.0.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -118,6 +118,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System">
|
||||
<Name>System</Name>
|
||||
</Reference>
|
||||
|
||||
Reference in New Issue
Block a user