added Picker Relations datatype - previously uComponents: MultiPicker Relations (renamed as can work with single item pickers as well as multipickers)
This commit is contained in:
@@ -15,6 +15,11 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string MultipleTextstringId = "5359AD0B-06CC-4182-92BD-0A9117448D3F";
|
public const string MultipleTextstringId = "5359AD0B-06CC-4182-92BD-0A9117448D3F";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Guid for the PickerRelations (previoulsy uComponents: MultiPickerRelations)
|
||||||
|
/// </summary>
|
||||||
|
public const string PickerRelationsId = "83396FF2-2E39-4A90-9066-17F5F3989374";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Guid for the Slider data-type.
|
/// Guid for the Slider data-type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web.UI;
|
||||||
|
using System.Web.UI.WebControls;
|
||||||
|
|
||||||
|
using umbraco.cms.businesslogic.datatype; // DefaultData
|
||||||
|
using umbraco.cms.businesslogic.property; // Property
|
||||||
|
using umbraco.cms.businesslogic.relation; // RelationType
|
||||||
|
using umbraco.interfaces; // IDataEditor
|
||||||
|
using UmbracoContent = umbraco.cms.businesslogic.Content; // UmbracoContent defined here so as to differentiate with System.Web.UI.WebControls.Content
|
||||||
|
|
||||||
|
namespace umbraco.editorControls.PickerRelations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class PickerRelationsDataEditor : CompositeControl, IDataEditor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// value stored by a datatype instance
|
||||||
|
/// </summary>
|
||||||
|
private IData data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// configuration options for this datatype, as defined by the PreValueEditor
|
||||||
|
/// </summary>
|
||||||
|
private PickerRelationsOptions options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Literal used to render status (Enabled || Disabled)
|
||||||
|
/// this datatype is only enabled and active if it can find the property alias on the current node (as defined in PreValueEditor) and
|
||||||
|
/// the relation type parent object type (or child if reverse index used) matches the object type of the current node which this datatype is on
|
||||||
|
/// </summary>
|
||||||
|
private Literal statusLiteral = new Literal();
|
||||||
|
|
||||||
|
|
||||||
|
private Literal jsLiteral = new Literal();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this is an RTE - this Property expected by Umbraco
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool TreatAsRichTextEditor
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the label should be shown when editing - this Property exected by Umbraco
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool ShowLabel
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the DataEditor - Property expected by Umbraco
|
||||||
|
/// </summary>
|
||||||
|
public Control Editor { get { return this; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the id of the current (content || media || member) node on which this datatype is a property
|
||||||
|
/// </summary>
|
||||||
|
private int CurrentContentId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((umbraco.cms.businesslogic.datatype.DefaultData)this.data).NodeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the UmbracoObjectType on which this datatype is a property of
|
||||||
|
/// </summary>
|
||||||
|
private uQuery.UmbracoObjectType CurrentContextObjectType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return uQuery.GetUmbracoObjectType(
|
||||||
|
uQuery.SqlHelper.ExecuteScalar<Guid>(
|
||||||
|
"SELECT nodeObjectType FROM umbracoNode WHERE id = @id",
|
||||||
|
uQuery.SqlHelper.CreateParameter("@id", this.CurrentContentId)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////// NOT CURRENTLY USED, BUT MIGHT BE USEFUL TO MARK BIDIRECTIONAL RELATIONS
|
||||||
|
/////// <summary>
|
||||||
|
/////// string to identify a particular instance of this datatype
|
||||||
|
/////// </summary>
|
||||||
|
////private string InstanceIdentifier
|
||||||
|
////{
|
||||||
|
//// get
|
||||||
|
//// {
|
||||||
|
//// Property pickerRelationsProperty = new Property(((DefaultData)this.data).PropertyId);
|
||||||
|
//// return "[" + pickerRelationsProperty.PropertyType.Id.ToString() + "]";
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of PickerRelationsDataEditor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">data stored by this instance of this datatype (not currently used)</param>
|
||||||
|
/// <param name="options">configuration options for this datatype as set by the PreValueEditor</param>
|
||||||
|
internal PickerRelationsDataEditor(IData data, PickerRelationsOptions options)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the child controls
|
||||||
|
/// </summary>
|
||||||
|
protected override void CreateChildControls()
|
||||||
|
{
|
||||||
|
this.statusLiteral.ID = "pickerRelations";
|
||||||
|
this.Controls.Add(this.statusLiteral); // displays if this datatype is valid in this context, and if so, which pickerProperty and relationtype it wires up
|
||||||
|
this.Controls.Add(this.jsLiteral);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
protected override void OnLoad(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoad(e);
|
||||||
|
this.EnsureChildControls();
|
||||||
|
|
||||||
|
this.statusLiteral.Text = "<span id=\"" + this.statusLiteral.ClientID + "\">";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.statusLiteral.Text += "Mapping: " + this.GetMappingDetails();
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
this.statusLiteral.Text += "Error: " + ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statusLiteral.Text += "</span>";
|
||||||
|
|
||||||
|
if (this.options.HideDataEditor)
|
||||||
|
{
|
||||||
|
this.jsLiteral.Text = @"
|
||||||
|
<script type=""text/javascript"">
|
||||||
|
jQuery(document).ready(function () {
|
||||||
|
jQuery('span#" + this.statusLiteral.ClientID + @"').parents('div.propertypane').first().hide();
|
||||||
|
});
|
||||||
|
</script>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by Umbraco when saving the node, this datatype doens't do anythign here, but with an event handler instead,
|
||||||
|
/// as needs to know the saved values of a sibling pickerProperty
|
||||||
|
/// </summary>
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns a string "Property '[propertyAlias]' with RelationType '[relationTypeName]"
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string GetMappingDetails()
|
||||||
|
{
|
||||||
|
string mappingDetails = string.Empty;
|
||||||
|
|
||||||
|
UmbracoContent currentContentNode = new UmbracoContent(this.CurrentContentId);
|
||||||
|
Property pickerProperty = currentContentNode.getProperty(this.options.PropertyAlias);
|
||||||
|
|
||||||
|
if (pickerProperty != null)
|
||||||
|
{
|
||||||
|
RelationType relationType = new RelationType(this.options.RelationTypeId); // Does it still exist ? TODO: check
|
||||||
|
|
||||||
|
if (this.IsContextUmbracoObjectTypeValid(this.CurrentContextObjectType, relationType))
|
||||||
|
{
|
||||||
|
mappingDetails = "Property '<strong>" + pickerProperty.PropertyType.Name + "</strong>' with " +
|
||||||
|
"Relation Type '<strong>" + relationType.Name + "</strong>'";
|
||||||
|
|
||||||
|
if(this.options.ReverseIndexing)
|
||||||
|
{
|
||||||
|
mappingDetails += " <i>(Reverse Index)</i>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Conflict with this Content Object Type and that expected by the Relation Type '" + relationType.Name + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Can't find a Property with the Alias '" + this.options.PropertyAlias + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappingDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// returns the UmbracoObjectType associated as defined by the supplied relation type, and if reverse indexing has been enabled
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relationType">associated RealationType</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public uQuery.UmbracoObjectType GetPickerUmbracoObjectType(RelationType relationType)
|
||||||
|
{
|
||||||
|
uQuery.UmbracoObjectType pickerUmbracoObjectType = uQuery.UmbracoObjectType.Unknown;
|
||||||
|
|
||||||
|
if (!relationType.Dual && this.options.ReverseIndexing)
|
||||||
|
{
|
||||||
|
pickerUmbracoObjectType = relationType.GetParentUmbracoObjectType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pickerUmbracoObjectType = relationType.GetChildUmbracoObjectType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pickerUmbracoObjectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check to see if the content id side of the relation type is valid (doesn't check the node picker id side)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="contextUmbracoObjectType">Type of the content object (content/ media or member)</param>
|
||||||
|
/// <param name="relationType">Type of the relation.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if [is content object type valid] [the specified content object type]; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public bool IsContextUmbracoObjectTypeValid(uQuery.UmbracoObjectType contextUmbracoObjectType, RelationType relationType)
|
||||||
|
{
|
||||||
|
bool isContextObjectTypeValid = false;
|
||||||
|
|
||||||
|
if (!relationType.Dual && this.options.ReverseIndexing)
|
||||||
|
{
|
||||||
|
// expects the current context to be the child in the relation
|
||||||
|
if (contextUmbracoObjectType == relationType.GetChildUmbracoObjectType())
|
||||||
|
{
|
||||||
|
isContextObjectTypeValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// expects the current context to be the parent in the relation
|
||||||
|
if (contextUmbracoObjectType == relationType.GetParentUmbracoObjectType())
|
||||||
|
{
|
||||||
|
isContextObjectTypeValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isContextObjectTypeValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// Insted of rendering the current name of this property, use a consistant label
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="writer"></param>
|
||||||
|
//protected override void Render(HtmlTextWriter writer)
|
||||||
|
//{
|
||||||
|
// writer.WriteLine("<div class='propertyItemheader'>MultiNode Relations</div>");
|
||||||
|
// writer.WriteLine("<div class='propertyItemContent'>");
|
||||||
|
// base.Render(writer);
|
||||||
|
// writer.WriteLine("</div>");
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
using umbraco.cms.businesslogic.datatype;
|
||||||
|
using umbraco.interfaces;
|
||||||
|
|
||||||
|
namespace umbraco.editorControls.PickerRelations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This datatype will render a CheckBoxList where the options are defined by an XPath expression,
|
||||||
|
/// </summary>
|
||||||
|
public class PickerRelationsDataType : umbraco.cms.businesslogic.datatype.BaseDataType, IDataType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
private PickerRelationsPreValueEditor preValueEditor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
private IDataEditor dataEditor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
private IData data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the data type.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The name of the data type.</value>
|
||||||
|
public override string DataTypeName { get { return "Picker Relations"; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the id.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The id.</value>
|
||||||
|
public override Guid Id { get { return new Guid(DataTypeGuids.PickerRelationsId); } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazy load the associated PreValueEditor instance,
|
||||||
|
/// this is constructed supplying 'this'
|
||||||
|
/// </summary>
|
||||||
|
public override IDataPrevalue PrevalueEditor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.preValueEditor == null)
|
||||||
|
{
|
||||||
|
this.preValueEditor = new PickerRelationsPreValueEditor(this);
|
||||||
|
}
|
||||||
|
return this.preValueEditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazy load the assocated DataEditor,
|
||||||
|
/// this is constructed supplying the data value stored by the PreValueEditor, and also the configuration settings of the PreValueEditor
|
||||||
|
/// </summary>
|
||||||
|
public override IDataEditor DataEditor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.dataEditor == null)
|
||||||
|
{
|
||||||
|
this.dataEditor = new PickerRelationsDataEditor(this.Data, ((PickerRelationsPreValueEditor)this.PrevalueEditor).Options);
|
||||||
|
}
|
||||||
|
return this.dataEditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazy load an empty DefaultData object, this is used to pass data between the PreValueEditor and the DataEditor
|
||||||
|
/// </summary>
|
||||||
|
public override IData Data
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.data == null)
|
||||||
|
{
|
||||||
|
this.data = new umbraco.cms.businesslogic.datatype.DefaultData(this);
|
||||||
|
}
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using umbraco.BusinessLogic; // ApplicationBase
|
||||||
|
using umbraco.cms.businesslogic; // SaveEventArgs
|
||||||
|
using umbraco.cms.businesslogic.media; // Media
|
||||||
|
using umbraco.cms.businesslogic.member; // Member
|
||||||
|
using umbraco.cms.businesslogic.web; // Documentusing umbraco.cms.businesslogic.propertytype;
|
||||||
|
using umbraco.cms.businesslogic.property;
|
||||||
|
using umbraco.cms.businesslogic.relation;
|
||||||
|
using umbraco.DataLayer;
|
||||||
|
|
||||||
|
namespace umbraco.editorControls.PickerRelations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event handler that will convert a CSV into Relations
|
||||||
|
/// </summary>
|
||||||
|
public class PickerRelationsEventHandler : ApplicationBase
|
||||||
|
{
|
||||||
|
private enum PickerStorageFormat
|
||||||
|
{
|
||||||
|
Csv,
|
||||||
|
Xml
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of PickerRelationsEventHandler,
|
||||||
|
/// hooks into the after event of saving a Content node, Media item or a Member
|
||||||
|
/// </summary>
|
||||||
|
public PickerRelationsEventHandler()
|
||||||
|
{
|
||||||
|
Document.AfterSave += new Document.SaveEventHandler(this.AfterSave);
|
||||||
|
Media.AfterSave += new Media.SaveEventHandler(this.AfterSave);
|
||||||
|
Member.AfterSave += new Member.SaveEventHandler(this.AfterSave);
|
||||||
|
|
||||||
|
Document.BeforeDelete += new Document.DeleteEventHandler(this.BeforeDelete);
|
||||||
|
Media.BeforeDelete += new Media.DeleteEventHandler(this.BeforeDelete);
|
||||||
|
Member.BeforeDelete += new Member.DeleteEventHandler(this.BeforeDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event after all properties have been saved
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
private void AfterSave(Content sender, SaveEventArgs e)
|
||||||
|
{
|
||||||
|
Guid pickerRelationsId = new Guid(DataTypeGuids.PickerRelationsId);
|
||||||
|
|
||||||
|
// For each PickerRelations datatype
|
||||||
|
foreach (Property pickerRelationsProperty in from property in sender.GenericProperties
|
||||||
|
where property.PropertyType.DataTypeDefinition.DataType.Id == pickerRelationsId
|
||||||
|
select property)
|
||||||
|
{
|
||||||
|
// used to identify this datatype instance - relations created are marked with this in the comment field
|
||||||
|
string instanceIdentifier = "[\"PropertyTypeId\":" + pickerRelationsProperty.PropertyType.Id.ToString() + "]";
|
||||||
|
|
||||||
|
// get configuration options for datatype
|
||||||
|
PickerRelationsOptions options = ((PickerRelationsPreValueEditor)pickerRelationsProperty.PropertyType.DataTypeDefinition.DataType.PrevalueEditor).Options;
|
||||||
|
|
||||||
|
// find Picker source propertyAlias field on sender
|
||||||
|
Property pickerProperty = sender.getProperty(options.PropertyAlias);
|
||||||
|
|
||||||
|
if (pickerProperty != null)
|
||||||
|
{
|
||||||
|
// get relationType from options
|
||||||
|
RelationType relationType = RelationType.GetById(options.RelationTypeId);
|
||||||
|
|
||||||
|
if (relationType != null)
|
||||||
|
{
|
||||||
|
// validate: 1) check current type of sender matches that expected by the relationType, validation method is in the DataEditor
|
||||||
|
uQuery.UmbracoObjectType contextObjectType = uQuery.UmbracoObjectType.Unknown;
|
||||||
|
switch (sender.GetType().ToString())
|
||||||
|
{
|
||||||
|
case "umbraco.cms.businesslogic.web.Document": contextObjectType = uQuery.UmbracoObjectType.Document; break;
|
||||||
|
case "umbraco.cms.businesslogic.media.Media": contextObjectType = uQuery.UmbracoObjectType.Media; break;
|
||||||
|
case "umbraco.cms.businesslogic.member.Member": contextObjectType = uQuery.UmbracoObjectType.Member; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((PickerRelationsDataEditor)pickerRelationsProperty.PropertyType.DataTypeDefinition.DataType.DataEditor)
|
||||||
|
.IsContextUmbracoObjectTypeValid(contextObjectType, relationType))
|
||||||
|
{
|
||||||
|
|
||||||
|
uQuery.UmbracoObjectType pickerUmbracoObjectType = uQuery.UmbracoObjectType.Unknown;
|
||||||
|
|
||||||
|
// Get the object type expected by the associated relation type and if this datatype has been configures as a rever index
|
||||||
|
pickerUmbracoObjectType = ((PickerRelationsDataEditor)pickerRelationsProperty.PropertyType.DataTypeDefinition.DataType.DataEditor)
|
||||||
|
.GetPickerUmbracoObjectType(relationType);
|
||||||
|
|
||||||
|
|
||||||
|
// clear all exisitng relations (or look to see previous verion of sender to delete changes ?)
|
||||||
|
DeleteRelations(relationType, sender.Id, options.ReverseIndexing, instanceIdentifier);
|
||||||
|
|
||||||
|
string pickerPropertyValue = pickerProperty.Value.ToString();
|
||||||
|
|
||||||
|
var pickerStorageFormat = PickerStorageFormat.Csv; // Assume default of csv
|
||||||
|
|
||||||
|
if (xmlHelper.CouldItBeXml(pickerPropertyValue))
|
||||||
|
{
|
||||||
|
pickerStorageFormat = PickerStorageFormat.Xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating instances of Documents / Media / Members ensures the IDs are of a valid type - be quicker to check with GetUmbracoObjectType(int)
|
||||||
|
Dictionary<int, string> pickerItems = null;
|
||||||
|
switch (pickerUmbracoObjectType)
|
||||||
|
{
|
||||||
|
case uQuery.UmbracoObjectType.Document:
|
||||||
|
switch (pickerStorageFormat)
|
||||||
|
{
|
||||||
|
case PickerStorageFormat.Csv:
|
||||||
|
pickerItems = uQuery.GetDocumentsByCsv(pickerPropertyValue).ToNameIds();
|
||||||
|
break;
|
||||||
|
case PickerStorageFormat.Xml:
|
||||||
|
pickerItems = uQuery.GetDocumentsByXml(pickerPropertyValue).ToNameIds();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case uQuery.UmbracoObjectType.Media:
|
||||||
|
switch (pickerStorageFormat)
|
||||||
|
{
|
||||||
|
case PickerStorageFormat.Csv:
|
||||||
|
pickerItems = uQuery.GetMediaByCsv(pickerPropertyValue).ToNameIds();
|
||||||
|
break;
|
||||||
|
case PickerStorageFormat.Xml:
|
||||||
|
pickerItems = uQuery.GetMediaByXml(pickerPropertyValue).ToNameIds();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case uQuery.UmbracoObjectType.Member:
|
||||||
|
switch (pickerStorageFormat)
|
||||||
|
{
|
||||||
|
case PickerStorageFormat.Csv:
|
||||||
|
pickerItems = uQuery.GetMembersByCsv(pickerPropertyValue).ToNameIds();
|
||||||
|
break;
|
||||||
|
case PickerStorageFormat.Xml:
|
||||||
|
pickerItems = uQuery.GetMembersByXml(pickerPropertyValue).ToNameIds();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pickerItems != null)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<int, string> pickerItem in pickerItems)
|
||||||
|
{
|
||||||
|
CreateRelation(relationType, sender.Id, pickerItem.Key, options.ReverseIndexing, instanceIdentifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error: content object type invalid with relation type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error: relation type is null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error: pickerProperty alias not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears any existing relations when deleting a node with a PickerRelations datatype
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The sender.</param>
|
||||||
|
/// <param name="e">The <see cref="umbraco.cms.businesslogic.DeleteEventArgs"/> instance containing the event data.</param>
|
||||||
|
private void BeforeDelete(Content sender, DeleteEventArgs e)
|
||||||
|
{
|
||||||
|
Guid pickerRelationsId = new Guid(DataTypeGuids.PickerRelationsId);
|
||||||
|
|
||||||
|
// Clean up any relations
|
||||||
|
|
||||||
|
// For each PickerRelations datatype
|
||||||
|
foreach (Property pickerRelationsProperty in from property in sender.GenericProperties
|
||||||
|
where property.PropertyType.DataTypeDefinition.DataType.Id == pickerRelationsId
|
||||||
|
select property)
|
||||||
|
{
|
||||||
|
// used to identify this datatype instance - relations created are marked with this in the comment field
|
||||||
|
string instanceIdentifier = "[\"PropertyTypeId\":" + pickerRelationsProperty.PropertyType.Id.ToString() + "]";
|
||||||
|
|
||||||
|
// get configuration options for datatype
|
||||||
|
PickerRelationsOptions options = ((PickerRelationsPreValueEditor)pickerRelationsProperty.PropertyType.DataTypeDefinition.DataType.PrevalueEditor).Options;
|
||||||
|
|
||||||
|
// get relationType from options
|
||||||
|
RelationType relationType = RelationType.GetById(options.RelationTypeId);
|
||||||
|
|
||||||
|
if (relationType != null)
|
||||||
|
{
|
||||||
|
// clear all exisitng relations
|
||||||
|
DeleteRelations(relationType, sender.Id, options.ReverseIndexing, instanceIdentifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete all relations using the content node for a given RelationType
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relationType"></param>
|
||||||
|
/// <param name="contentNodeId"></param>
|
||||||
|
/// <param name="reverseIndexing"></param>
|
||||||
|
/// <param name="instanceIdentifier">NOT USED ATM</param>
|
||||||
|
private static void DeleteRelations(RelationType relationType, int contentNodeId, bool reverseIndexing, string instanceIdentifier)
|
||||||
|
{
|
||||||
|
//if relationType is bi-directional or a reverse index then we can't get at the relations via the API, so using SQL
|
||||||
|
string getRelationsSql = "SELECT id FROM umbracoRelation WHERE relType = " + relationType.Id.ToString() + " AND ";
|
||||||
|
|
||||||
|
if (reverseIndexing || relationType.Dual)
|
||||||
|
{
|
||||||
|
getRelationsSql += "childId = " + contentNodeId.ToString();
|
||||||
|
}
|
||||||
|
if (relationType.Dual) // need to return relations where content node id is used on both sides
|
||||||
|
{
|
||||||
|
getRelationsSql += " OR ";
|
||||||
|
}
|
||||||
|
if (!reverseIndexing || relationType.Dual)
|
||||||
|
{
|
||||||
|
getRelationsSql += "parentId = " + contentNodeId.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
getRelationsSql += " AND comment = '" + instanceIdentifier + "'";
|
||||||
|
|
||||||
|
using (IRecordsReader relations = uQuery.SqlHelper.ExecuteReader(getRelationsSql))
|
||||||
|
{
|
||||||
|
//clear data
|
||||||
|
Relation relation;
|
||||||
|
if (relations.HasRecords)
|
||||||
|
{
|
||||||
|
while (relations.Read())
|
||||||
|
{
|
||||||
|
relation = new Relation(relations.GetInt("id"));
|
||||||
|
|
||||||
|
// TODO: [HR] check to see if an instance identifier is used
|
||||||
|
relation.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relationType"></param>
|
||||||
|
/// <param name="contentNodeId">id sourced from the Content / Media / Member</param>
|
||||||
|
/// <param name="pickerNodeId">id sourced from the Picker</param>
|
||||||
|
/// <param name="reverseIndexing">if true, reverses the parentId and child Id</param>
|
||||||
|
/// <param name="instanceIdentifier">JSON string with id of Picker Relations property instance</param>
|
||||||
|
private static void CreateRelation(RelationType relationType, int contentNodeId, int pickerNodeId, bool reverseIndexing, string instanceIdentifier)
|
||||||
|
{
|
||||||
|
if (reverseIndexing)
|
||||||
|
{
|
||||||
|
Relation.MakeNew(pickerNodeId, contentNodeId, relationType, instanceIdentifier);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Relation.MakeNew(contentNodeId, pickerNodeId, relationType, instanceIdentifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace umbraco.editorControls.PickerRelations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data Class, used to store the configuration options for the PickerRelationsPreValueEditor
|
||||||
|
/// </summary>
|
||||||
|
internal class PickerRelationsOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Alias of the pickerProperty to get a csv value of IDs from //TODO: a known format for xml fragments would be good too
|
||||||
|
/// </summary>
|
||||||
|
public string PropertyAlias { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Id of the RelationType to use
|
||||||
|
/// </summary>
|
||||||
|
public int RelationTypeId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// only relevant with parent-child
|
||||||
|
/// </summary>
|
||||||
|
public bool ReverseIndexing { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// if true then the property is hidden
|
||||||
|
/// </summary>
|
||||||
|
public bool HideDataEditor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an instance of PickerRelationsOptions
|
||||||
|
/// </summary>
|
||||||
|
public PickerRelationsOptions()
|
||||||
|
{
|
||||||
|
// Default values
|
||||||
|
this.PropertyAlias = string.Empty;
|
||||||
|
this.RelationTypeId = -1;
|
||||||
|
this.ReverseIndexing = false;
|
||||||
|
this.HideDataEditor = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web.UI;
|
||||||
|
using System.Web.UI.WebControls;
|
||||||
|
using umbraco.cms.businesslogic.datatype;
|
||||||
|
using umbraco.cms.businesslogic.relation;
|
||||||
|
using umbraco.macroRenderings;
|
||||||
|
|
||||||
|
namespace umbraco.editorControls.PickerRelations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This PreValueEditor will require an XPath expression to define the nodes to pick as CheckBox options,
|
||||||
|
/// TODO: [HR] min / max selections ?
|
||||||
|
/// Uses the shared JsonPreValueEditor as nice way of lightweight serializing a config data class object into a single DB field
|
||||||
|
/// </summary>
|
||||||
|
public class PickerRelationsPreValueEditor : AbstractJsonPrevalueEditor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prepopulated Umbraco Propery Picker, lists all aliases (could refine this by asking for the context in which this relation wire-up will
|
||||||
|
/// be used, and then only listing the aliases for that context)
|
||||||
|
/// </summary>
|
||||||
|
private propertyTypePicker pickerPropertyAliasPicker = new propertyTypePicker();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RequiredFieldValidator for the ProperyAliasPicker
|
||||||
|
/// </summary>
|
||||||
|
private RequiredFieldValidator pickerPropertyAliasRequiredFieldValidator = new RequiredFieldValidator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// drop down list of all relation types
|
||||||
|
/// </summary>
|
||||||
|
private DropDownList relationTypeDropDownList = new DropDownList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RequiredFieldValidator for the RelationType DropDownList
|
||||||
|
/// </summary>
|
||||||
|
private RequiredFieldValidator relationTypeRequiredFieldValidator = new RequiredFieldValidator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If a parent to child relation type is selected, then this checkbox will indicate the direction to use,
|
||||||
|
/// with reverse indexing the parents are the nodes selected via the picker, and the nodeID on which the
|
||||||
|
/// this datatype is used, become the child nodes
|
||||||
|
/// </summary>
|
||||||
|
private CheckBox reverseIndexingCheckBox = new CheckBox();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// if selected, then the property on the data editor is hidden (it's only used as a label)
|
||||||
|
/// </summary>
|
||||||
|
private CheckBox hideDataEditorCheckBox = new CheckBox();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data object used to define the configuration status of this PreValueEditor
|
||||||
|
/// </summary>
|
||||||
|
private PickerRelationsOptions options = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Currently selected RelationType
|
||||||
|
/// </summary>
|
||||||
|
private RelationType relationType = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazy load the options data object that represents the current state of this datatypes configuration
|
||||||
|
/// </summary>
|
||||||
|
internal PickerRelationsOptions Options
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.options == null)
|
||||||
|
{
|
||||||
|
// Deserialize any stored settings for this PreValueEditor instance
|
||||||
|
this.options = this.GetPreValueOptions<PickerRelationsOptions>();
|
||||||
|
|
||||||
|
// If still null, ie, object couldn't be de-serialized from PreValue[0] string value
|
||||||
|
if (this.options == null)
|
||||||
|
{
|
||||||
|
// Create a new Options data object with the default values
|
||||||
|
this.options = new PickerRelationsOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazy load currently selected RelationType
|
||||||
|
/// </summary>
|
||||||
|
private RelationType RelationType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.relationType == null)
|
||||||
|
{
|
||||||
|
// Attempt to get RelationType from the selected DropDownList item
|
||||||
|
int relationTypeId;
|
||||||
|
if(int.TryParse(this.relationTypeDropDownList.SelectedValue, out relationTypeId))
|
||||||
|
{
|
||||||
|
if (relationTypeId != -1)
|
||||||
|
{
|
||||||
|
this.relationType = new RelationType(relationTypeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.relationType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a new instance of PickerRelationsPreValueEditor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataType">PickerRelationsDataType</param>
|
||||||
|
public PickerRelationsPreValueEditor(umbraco.cms.businesslogic.datatype.BaseDataType dataType)
|
||||||
|
: base(dataType, umbraco.cms.businesslogic.datatype.DBTypes.Nvarchar)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates all of the controls and assigns all of their properties
|
||||||
|
/// </summary>
|
||||||
|
protected override void CreateChildControls()
|
||||||
|
{
|
||||||
|
this.pickerPropertyAliasPicker.ID = "pickerPropertyAliasPicker";
|
||||||
|
|
||||||
|
this.pickerPropertyAliasRequiredFieldValidator.Text = " Required";
|
||||||
|
this.pickerPropertyAliasRequiredFieldValidator.InitialValue = string.Empty;
|
||||||
|
this.pickerPropertyAliasRequiredFieldValidator.ControlToValidate = this.pickerPropertyAliasPicker.ID;
|
||||||
|
|
||||||
|
this.relationTypeDropDownList.ID = "relationTypeDropDownList";
|
||||||
|
this.relationTypeDropDownList.AutoPostBack = true;
|
||||||
|
this.relationTypeDropDownList.DataSource = RelationType.GetAll().OrderBy(x => x.Name);
|
||||||
|
this.relationTypeDropDownList.DataTextField = "Name";
|
||||||
|
this.relationTypeDropDownList.DataValueField = "Id";
|
||||||
|
this.relationTypeDropDownList.DataBind();
|
||||||
|
this.relationTypeDropDownList.Items.Insert(0, new ListItem(string.Empty, "-1"));
|
||||||
|
|
||||||
|
this.relationTypeRequiredFieldValidator.Text = " Required";
|
||||||
|
this.relationTypeRequiredFieldValidator.InitialValue = "-1";
|
||||||
|
this.relationTypeRequiredFieldValidator.ControlToValidate = this.relationTypeDropDownList.ID;
|
||||||
|
|
||||||
|
this.Controls.Add(this.pickerPropertyAliasPicker);
|
||||||
|
this.Controls.Add(this.pickerPropertyAliasRequiredFieldValidator);
|
||||||
|
this.Controls.Add(this.relationTypeDropDownList);
|
||||||
|
this.Controls.Add(this.relationTypeRequiredFieldValidator);
|
||||||
|
this.Controls.Add(this.reverseIndexingCheckBox);
|
||||||
|
this.Controls.Add(this.hideDataEditorCheckBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
protected override void OnLoad(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoad(e);
|
||||||
|
|
||||||
|
if (!this.Page.IsPostBack)
|
||||||
|
{
|
||||||
|
// Read in stored configuration values
|
||||||
|
if (this.pickerPropertyAliasPicker.Items.Contains(new ListItem(this.Options.PropertyAlias)))
|
||||||
|
{
|
||||||
|
this.pickerPropertyAliasPicker.SelectedValue = this.Options.PropertyAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.relationTypeDropDownList.Items.FindByValue(this.Options.RelationTypeId.ToString()) != null)
|
||||||
|
{
|
||||||
|
this.relationTypeDropDownList.SelectedValue = this.Options.RelationTypeId.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reverseIndexingCheckBox.Checked = this.Options.ReverseIndexing;
|
||||||
|
|
||||||
|
this.hideDataEditorCheckBox.Checked = this.Options.HideDataEditor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the pre value data to Umbraco
|
||||||
|
/// </summary>
|
||||||
|
public override void Save()
|
||||||
|
{
|
||||||
|
if (this.Page.IsValid)
|
||||||
|
{
|
||||||
|
this.Options.PropertyAlias = this.pickerPropertyAliasPicker.SelectedValue;
|
||||||
|
if (this.RelationType != null)
|
||||||
|
{
|
||||||
|
this.Options.RelationTypeId = this.RelationType.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Options.ReverseIndexing = this.reverseIndexingCheckBox.Checked;
|
||||||
|
|
||||||
|
this.Options.HideDataEditor = this.hideDataEditorCheckBox.Checked;
|
||||||
|
|
||||||
|
// Serialize to Umbraco database field
|
||||||
|
this.SaveAsJson(this.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to remove styling from the built in pickerProperty alias picker DropDownList
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
protected override void OnPreRender(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnPreRender(e);
|
||||||
|
|
||||||
|
this.pickerPropertyAliasPicker.CssClass = string.Empty; // Remove guiInputTextStandard
|
||||||
|
|
||||||
|
// Sort properties in the built in property picker control
|
||||||
|
ListItem[] propertyAliasListItems = this.pickerPropertyAliasPicker.Items.Cast<ListItem>().OrderBy(x => x.Text).ToArray();
|
||||||
|
|
||||||
|
this.pickerPropertyAliasPicker.Items.Clear();
|
||||||
|
this.pickerPropertyAliasPicker.Items.AddRange(propertyAliasListItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces the base class writer and instead uses the shared uComponents extension method, to inject consistant markup
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
protected override void RenderContents(HtmlTextWriter writer)
|
||||||
|
{
|
||||||
|
writer.AddPrevalueRow("Picker Alias", this.pickerPropertyAliasPicker, this.pickerPropertyAliasRequiredFieldValidator);
|
||||||
|
writer.AddPrevalueRow("Relation Type", this.relationTypeDropDownList, this.relationTypeRequiredFieldValidator);
|
||||||
|
|
||||||
|
// Only show this field if selected RelationType is of Parent to Child
|
||||||
|
if (this.RelationType != null)
|
||||||
|
{
|
||||||
|
if (!this.RelationType.Dual)
|
||||||
|
{
|
||||||
|
writer.AddPrevalueRow("Reverse Indexing", this.reverseIndexingCheckBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.AddPrevalueRow("Hide Data Editor", this.hideDataEditorCheckBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -156,6 +156,10 @@
|
|||||||
<Project>{651E1350-91B6-44B7-BD60-7207006D7003}</Project>
|
<Project>{651E1350-91B6-44B7-BD60-7207006D7003}</Project>
|
||||||
<Name>umbraco.presentation</Name>
|
<Name>umbraco.presentation</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\macroRenderings\umbraco.macroRenderings.csproj">
|
||||||
|
<Project>{52AB8F1F-FB76-4E8C-885F-0747B6CE71EC}</Project>
|
||||||
|
<Name>umbraco.macroRenderings</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\umbraco.controls\umbraco.controls.csproj">
|
<ProjectReference Include="..\umbraco.controls\umbraco.controls.csproj">
|
||||||
<Project>{6EDD2061-82F2-461B-BB6E-879245A832DE}</Project>
|
<Project>{6EDD2061-82F2-461B-BB6E-879245A832DE}</Project>
|
||||||
<Name>umbraco.controls</Name>
|
<Name>umbraco.controls</Name>
|
||||||
@@ -314,6 +318,11 @@
|
|||||||
<Compile Include="pagepicker\PagePickerDataType.cs">
|
<Compile Include="pagepicker\PagePickerDataType.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="PickerRelations\PickerRelationsDataEditor.cs" />
|
||||||
|
<Compile Include="PickerRelations\PickerRelationsDataType.cs" />
|
||||||
|
<Compile Include="PickerRelations\PickerRelationsEventHandler.cs" />
|
||||||
|
<Compile Include="PickerRelations\PickerRelationsOptions.cs" />
|
||||||
|
<Compile Include="PickerRelations\PickerRelationsPreValueEditor.cs" />
|
||||||
<Compile Include="PrevalueEditorExtensions.cs" />
|
<Compile Include="PrevalueEditorExtensions.cs" />
|
||||||
<Compile Include="radiobuttonlist\radiobox.cs">
|
<Compile Include="radiobuttonlist\radiobox.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
|
|||||||
Reference in New Issue
Block a user