Skip to content

Commit 81e56be

Browse files
committed
Implementing Document Type Compositions in the Document Type Editor.
Adds changes to the CMSNode and doc type related class which improves the internal usage of the new model based on IUmbracoEntity.
1 parent 52457fe commit 81e56be

File tree

11 files changed

+258
-69
lines changed

11 files changed

+258
-69
lines changed

src/Umbraco.Core/Models/ContentTypeCompositionBase.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,17 @@ public bool RemoveContentType(string alias)
9393
{
9494
if (ContentTypeCompositionExists(alias))
9595
{
96-
var contentTypeComposition = ContentTypeComposition.First(x => x.Alias == alias);
96+
var contentTypeComposition = ContentTypeComposition.FirstOrDefault(x => x.Alias == alias);
97+
if (contentTypeComposition == null)//You can't remove a composition from another composition
98+
return false;
99+
97100
RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id);
101+
102+
//If the ContentType we are removing has Compositions of its own these needs to be removed as well
103+
var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList();
104+
if(compositionIdsToRemove.Any())
105+
RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove);
106+
98107
OnPropertyChanged(ContentTypeCompositionSelector);
99108
return _contentTypeComposition.Remove(contentTypeComposition);
100109
}

src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeCom
240240
{
241241
var nodeId = contentDto.NodeId;
242242
var propertyTypeId = propertyType.Id;
243-
var propertySql = new Sql().Select("*")
243+
var propertySql = new Sql().Select("cmsPropertyData.id")
244244
.From<PropertyDataDto>()
245245
.InnerJoin<PropertyTypeDto>()
246246
.On<PropertyDataDto, PropertyTypeDto>(
@@ -249,7 +249,7 @@ protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeCom
249249
.Where<PropertyTypeDto>(x => x.Id == propertyTypeId);
250250

251251
//Finally delete the properties that match our criteria for removing a ContentType from the composition
252-
Database.Delete<PropertyDataDto>(propertySql);
252+
Database.Delete<PropertyDataDto>(new Sql("WHERE id IN (" + propertySql.SQL + ")", propertySql.Arguments));
253253
}
254254
}
255255
}
@@ -285,7 +285,8 @@ protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeCom
285285
}
286286
}
287287

288-
if (((ICanBeDirty) entity).IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
288+
if (((ICanBeDirty)entity).IsPropertyDirty("PropertyGroups") ||
289+
entity.PropertyGroups.Any(x => x.IsDirty()))
289290
{
290291
//Delete Tabs/Groups by excepting entries from db with entries from collections
291292
var dbPropertyGroups =
@@ -346,6 +347,25 @@ protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeCom
346347
if (propertyType.HasIdentity == false)
347348
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
348349
}
350+
351+
//If a Composition is removed we need to update/reset references to the PropertyGroups on that ContentType
352+
if (((ICanBeDirty)entity).IsPropertyDirty("ContentTypeComposition") &&
353+
compositionBase != null &&
354+
compositionBase.RemovedContentTypeKeyTracker != null &&
355+
compositionBase.RemovedContentTypeKeyTracker.Any())
356+
{
357+
foreach (var compositionId in compositionBase.RemovedContentTypeKeyTracker)
358+
{
359+
var dbPropertyGroups =
360+
Database.Fetch<PropertyTypeGroupDto>("WHERE contenttypeNodeId = @Id", new { Id = compositionId })
361+
.Select(x => x.Id);
362+
foreach (var propertyGroup in dbPropertyGroups)
363+
{
364+
Database.Update<PropertyTypeGroupDto>("SET parentGroupId = NULL WHERE parentGroupId = @TabId AND contenttypeNodeId = @ContentTypeNodeId",
365+
new { TabId = propertyGroup, ContentTypeNodeId = entity.Id });
366+
}
367+
}
368+
}
349369
}
350370

351371
protected IEnumerable<ContentTypeSort> GetAllowedContentTypeIds(int id)

src/Umbraco.Web.UI/umbraco/config/lang/en.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
</area>
246246
<area alias="editcontenttype">
247247
<key alias="allowedchildnodetypes">Allowed child node types</key>
248+
<key alias="contenttypecompositions">Document Type Compositions</key>
248249
<key alias="create">Create</key>
249250
<key alias="deletetab">Delete tab</key>
250251
<key alias="description">Description</key>

src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251

252252
<area alias="editcontenttype">
253253
<key alias="allowedchildnodetypes">Allowed child nodetypes</key>
254+
<key alias="contenttypecompositions">Document Type Compositions</key>
254255
<key alias="create">Create</key>
255256
<key alias="deletetab">Delete tab</key>
256257
<key alias="description">Description</key>

src/Umbraco.Web.UI/umbraco/controls/ContentTypeControlNew.ascx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,20 @@
8181
<asp:CheckBox runat="server" ID="cb_isContainer" Text="Yes" /><br />
8282
</cc2:PropertyPanel>
8383
</cc2:Pane>
84-
<cc2:Pane ID="Pane5" runat="server">
85-
86-
<cc2:PropertyPanel ID="pp_allowedChildren" runat="server" Text="Allowed Child nodetypes">
87-
<asp:CheckBoxList ID="lstAllowedContentTypes" runat="server" EnableViewState="True"/>
88-
<asp:PlaceHolder ID="PlaceHolderAllowedContentTypes" runat="server"/>
89-
</cc2:PropertyPanel>
90-
</cc2:Pane>
84+
85+
<cc2:Pane ID="Pane5" runat="server">
86+
<cc2:PropertyPanel ID="pp_allowedChildren" runat="server" Text="Allowed Child nodetypes">
87+
<asp:CheckBoxList ID="lstAllowedContentTypes" runat="server" EnableViewState="True"/>
88+
<asp:PlaceHolder ID="PlaceHolderAllowedContentTypes" runat="server"/>
89+
</cc2:PropertyPanel>
90+
</cc2:Pane>
91+
92+
<cc2:Pane ID="Pane9" runat="server">
93+
<cc2:PropertyPanel ID="pp_compositions" runat="server" Text="ContentType Compositions">
94+
<asp:CheckBoxList ID="lstContentTypeCompositions" runat="server" EnableViewState="True"/>
95+
<asp:PlaceHolder ID="PlaceHolderContentTypeCompositions" runat="server"/>
96+
</cc2:PropertyPanel>
97+
</cc2:Pane>
9198
</asp:Panel>
9299

93100

src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs

Lines changed: 140 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public partial class ContentTypeControlNew : UmbracoUserControl
5454
// "Structure" tab
5555
protected DualSelectbox DualAllowedContentTypes = new DualSelectbox();
5656

57+
// "Structure" tab - Compositions
58+
protected DualSelectbox DualContentTypeCompositions = new DualSelectbox();
59+
5760
// "Info" tab
5861
public uicontrols.TabPage InfoTabPage;
5962

@@ -88,6 +91,7 @@ override protected void OnInit(EventArgs e)
8891
else
8992
{
9093
SetupStructurePane();
94+
SetupCompositionsPane();
9195
}
9296

9397
SetupGenericPropertiesPane();
@@ -105,7 +109,7 @@ protected void Page_Load(object sender, EventArgs e)
105109
pp_alias.Text = ui.Text("alias", Security.CurrentUser);
106110
pp_name.Text = ui.Text("name", Security.CurrentUser);
107111
pp_allowedChildren.Text = ui.Text("allowedchildnodetypes", Security.CurrentUser);
108-
112+
pp_compositions.Text = ui.Text("contenttypecompositions", Security.CurrentUser);
109113
pp_description.Text = ui.Text("editcontenttype", "description", Security.CurrentUser);
110114
pp_icon.Text = ui.Text("icon", Security.CurrentUser);
111115

@@ -291,20 +295,62 @@ protected void save_click(object sender, EventArgs e)
291295
Trace.Write("ContentTypeControlNew", "executing task");
292296

293297
//we need to re-set the UmbracoContext since it will be nulled and our cache handlers need it
294-
global::Umbraco.Web.UmbracoContext.Current = asyncState.UmbracoContext;
298+
//global::Umbraco.Web.UmbracoContext.Current = asyncState.UmbracoContext;
295299

296300
_contentType.ContentTypeItem.Name = txtName.Text;
297301
_contentType.ContentTypeItem.Alias = txtAlias.Text; // raw, contentType.Alias takes care of it
298302
_contentType.ContentTypeItem.Icon = tb_icon.Value;
299303
_contentType.ContentTypeItem.Description = description.Text;
300-
//_contentType.ContentTypeItem.Thumbnail = ddlThumbnails.SelectedValue;
304+
//_contentType.ContentTypeItem.Thumbnail = ddlThumbnails.SelectedValue;
301305
_contentType.ContentTypeItem.AllowedAsRoot = allowAtRoot.Checked;
302306
_contentType.ContentTypeItem.IsContainer = cb_isContainer.Checked;
303307

304308
int i = 0;
305309
var ids = SaveAllowedChildTypes();
306310
_contentType.ContentTypeItem.AllowedContentTypes = ids.Select(x => new ContentTypeSort {Id = new Lazy<int>(() => x), SortOrder = i++});
307311

312+
//Saving ContentType Compositions
313+
var compositionIds = SaveCompositionContentTypes();
314+
var existingCompsitionIds = _contentType.ContentTypeItem.CompositionIds().ToList();
315+
if (compositionIds.Any())
316+
{
317+
//Iterate ContentType Ids from the save-collection
318+
foreach (var compositionId in compositionIds)
319+
{
320+
//If the compositionId is the Id of the current ContentType we skip it
321+
if(_contentType.Id.Equals(compositionId)) continue;
322+
323+
//If the Id already exists we'll just skip it
324+
if (existingCompsitionIds.Any(x => x.Equals(compositionId))) continue;
325+
326+
//New Ids will get added to the collection
327+
var compositionType = Services.ContentTypeService.GetContentType(compositionId);
328+
var added = _contentType.ContentTypeItem.AddContentType(compositionType);
329+
//TODO if added=false then return error message
330+
}
331+
332+
//Iterate the set except of existing and new Ids
333+
var removeIds = existingCompsitionIds.Except(compositionIds);
334+
foreach (var removeId in removeIds)
335+
{
336+
//Remove ContentTypes that was deselected in the list
337+
var compositionType = Services.ContentTypeService.GetContentType(removeId);
338+
var removed = _contentType.ContentTypeItem.RemoveContentType(compositionType.Alias);
339+
}
340+
}
341+
else if (existingCompsitionIds.Any())
342+
{
343+
//Iterate the set except of existing and new Ids
344+
var removeIds = existingCompsitionIds.Except(compositionIds);
345+
foreach (var removeId in removeIds)
346+
{
347+
//Remove ContentTypes that was deselected in the list
348+
var compositionType = Services.ContentTypeService.GetContentType(removeId);
349+
var removed = _contentType.ContentTypeItem.RemoveContentType(compositionType.Alias);
350+
}
351+
}
352+
353+
308354
var tabs = SaveTabs();
309355
foreach (var tab in tabs)
310356
{
@@ -319,6 +365,7 @@ protected void save_click(object sender, EventArgs e)
319365
}
320366

321367
SavePropertyType(asyncState.SaveArgs, _contentType.ContentTypeItem);
368+
//SavePropertyType(state.SaveArgs, _contentType.ContentTypeItem);
322369
UpdatePropertyTypes(_contentType.ContentTypeItem);
323370

324371
if (DocumentTypeCallback != null)
@@ -342,8 +389,10 @@ protected void save_click(object sender, EventArgs e)
342389
catch (DuplicateNameException ex)
343390
{
344391
DuplicateAliasValidator.IsValid = false;
345-
asyncState.SaveArgs.IconType = BasePage.speechBubbleIcon.error;
346-
asyncState.SaveArgs.Message = ex.Message;
392+
//asyncState.SaveArgs.IconType = BasePage.speechBubbleIcon.error;
393+
state.SaveArgs.IconType = BasePage.speechBubbleIcon.error;
394+
//asyncState.SaveArgs.Message = ex.Message;
395+
state.SaveArgs.Message = ex.Message;
347396
return;
348397
}
349398

@@ -580,6 +629,56 @@ private int[] SaveAllowedChildTypes()
580629

581630
#endregion
582631

632+
#region Compositions Pane
633+
634+
private void SetupCompositionsPane()
635+
{
636+
DualContentTypeCompositions.ID = "compositionContentTypes";
637+
DualContentTypeCompositions.Width = 175;
638+
639+
int[] compositionIds = _contentType.ContentTypeItem.CompositionIds().ToArray();
640+
if (!Page.IsPostBack)
641+
{
642+
string chosenContentTypeIDs = "";
643+
ContentType[] contentTypes = _contentType.GetAll();
644+
foreach (ContentType ct in contentTypes.OrderBy(x => x.Text))
645+
{
646+
ListItem li = new ListItem(ct.Text, ct.Id.ToString());
647+
if (ct.Id == _contentType.Id)
648+
li.Enabled = false;
649+
650+
DualContentTypeCompositions.Items.Add(li);
651+
lstContentTypeCompositions.Items.Add(li);
652+
653+
foreach (int i in compositionIds)
654+
{
655+
if (i == ct.Id)
656+
{
657+
li.Selected = true;
658+
chosenContentTypeIDs += ct.Id + ",";
659+
}
660+
}
661+
}
662+
DualContentTypeCompositions.Value = chosenContentTypeIDs;
663+
}
664+
}
665+
666+
private int[] SaveCompositionContentTypes()
667+
{
668+
var tmp = new ArrayList();
669+
foreach (ListItem li in lstContentTypeCompositions.Items)
670+
{
671+
if (li.Selected)
672+
tmp.Add(int.Parse(li.Value));
673+
}
674+
var ids = new int[tmp.Count];
675+
for (int i = 0; i < tmp.Count; i++) ids[i] = (int)tmp[i];
676+
677+
return ids;
678+
}
679+
680+
#endregion
681+
583682
#region "Generic properties" Pane
584683

585684
private void SetupGenericPropertiesPane()
@@ -1625,5 +1724,41 @@ protected void dgTabs_itemdatabound(object sender, DataGridItemEventArgs e)
16251724
/// To modify move field declaration from designer file to code-behind file.
16261725
/// </remarks>
16271726
protected global::System.Web.UI.WebControls.CustomValidator DuplicateAliasValidator;
1727+
1728+
/// <summary>
1729+
/// Pane9 control.
1730+
/// </summary>
1731+
/// <remarks>
1732+
/// Auto-generated field.
1733+
/// To modify move field declaration from designer file to code-behind file.
1734+
/// </remarks>
1735+
protected global::umbraco.uicontrols.Pane Pane9;
1736+
1737+
/// <summary>
1738+
/// pp_compositions control.
1739+
/// </summary>
1740+
/// <remarks>
1741+
/// Auto-generated field.
1742+
/// To modify move field declaration from designer file to code-behind file.
1743+
/// </remarks>
1744+
protected global::umbraco.uicontrols.PropertyPanel pp_compositions;
1745+
1746+
/// <summary>
1747+
/// lstContentTypeCompositions control.
1748+
/// </summary>
1749+
/// <remarks>
1750+
/// Auto-generated field.
1751+
/// To modify move field declaration from designer file to code-behind file.
1752+
/// </remarks>
1753+
protected global::System.Web.UI.WebControls.CheckBoxList lstContentTypeCompositions;
1754+
1755+
/// <summary>
1756+
/// PlaceHolderContentTypeCompositions control.
1757+
/// </summary>
1758+
/// <remarks>
1759+
/// Auto-generated field.
1760+
/// To modify move field declaration from designer file to code-behind file.
1761+
/// </remarks>
1762+
protected global::System.Web.UI.WebControls.PlaceHolder PlaceHolderContentTypeCompositions;
16281763
}
16291764
}

0 commit comments

Comments
 (0)