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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user