diff --git a/components/editorControls/DataTypeGuids.cs b/components/editorControls/DataTypeGuids.cs index 317963738a..85db68af85 100644 --- a/components/editorControls/DataTypeGuids.cs +++ b/components/editorControls/DataTypeGuids.cs @@ -15,6 +15,11 @@ /// public const string MultipleTextstringId = "5359AD0B-06CC-4182-92BD-0A9117448D3F"; + /// + /// Guid for the PickerRelations (previoulsy uComponents: MultiPickerRelations) + /// + public const string PickerRelationsId = "83396FF2-2E39-4A90-9066-17F5F3989374"; + /// /// Guid for the Slider data-type. /// diff --git a/components/editorControls/PickerRelations/PickerRelationsDataEditor.cs b/components/editorControls/PickerRelations/PickerRelationsDataEditor.cs new file mode 100644 index 0000000000..e95f2e5789 --- /dev/null +++ b/components/editorControls/PickerRelations/PickerRelationsDataEditor.cs @@ -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 +{ + /// + /// + /// + public class PickerRelationsDataEditor : CompositeControl, IDataEditor + { + /// + /// value stored by a datatype instance + /// + private IData data; + + /// + /// configuration options for this datatype, as defined by the PreValueEditor + /// + private PickerRelationsOptions options; + + /// + /// 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 + /// + private Literal statusLiteral = new Literal(); + + + private Literal jsLiteral = new Literal(); + + /// + /// Gets a value indicating whether this is an RTE - this Property expected by Umbraco + /// + public virtual bool TreatAsRichTextEditor + { + get { return false; } + } + + /// + /// Gets a value indicating whether the label should be shown when editing - this Property exected by Umbraco + /// + public virtual bool ShowLabel + { + get { return true; } + } + + /// + /// Gets the DataEditor - Property expected by Umbraco + /// + public Control Editor { get { return this; } } + + /// + /// Gets the id of the current (content || media || member) node on which this datatype is a property + /// + private int CurrentContentId + { + get + { + return ((umbraco.cms.businesslogic.datatype.DefaultData)this.data).NodeId; + } + } + + /// + /// Gets the UmbracoObjectType on which this datatype is a property of + /// + private uQuery.UmbracoObjectType CurrentContextObjectType + { + get + { + return uQuery.GetUmbracoObjectType( + uQuery.SqlHelper.ExecuteScalar( + "SELECT nodeObjectType FROM umbracoNode WHERE id = @id", + uQuery.SqlHelper.CreateParameter("@id", this.CurrentContentId))); + } + } + + /////// NOT CURRENTLY USED, BUT MIGHT BE USEFUL TO MARK BIDIRECTIONAL RELATIONS + /////// + /////// string to identify a particular instance of this datatype + /////// + ////private string InstanceIdentifier + ////{ + //// get + //// { + //// Property pickerRelationsProperty = new Property(((DefaultData)this.data).PropertyId); + //// return "[" + pickerRelationsProperty.PropertyType.Id.ToString() + "]"; + //// } + ////} + + /// + /// Initializes a new instance of PickerRelationsDataEditor + /// + /// data stored by this instance of this datatype (not currently used) + /// configuration options for this datatype as set by the PreValueEditor + internal PickerRelationsDataEditor(IData data, PickerRelationsOptions options) + { + this.data = data; + this.options = options; + } + + /// + /// Creates the child controls + /// + 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); + } + + /// + /// + /// + /// + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + this.EnsureChildControls(); + + this.statusLiteral.Text = ""; + + try + { + this.statusLiteral.Text += "Mapping: " + this.GetMappingDetails(); + } + catch(Exception ex) + { + this.statusLiteral.Text += "Error: " + ex.Message; + } + + this.statusLiteral.Text += ""; + + if (this.options.HideDataEditor) + { + this.jsLiteral.Text = @" + "; + } + } + + /// + /// 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 + /// + public void Save() + { + } + + /// + /// returns a string "Property '[propertyAlias]' with RelationType '[relationTypeName]" + /// + /// + 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 '" + pickerProperty.PropertyType.Name + "' with " + + "Relation Type '" + relationType.Name + "'"; + + if(this.options.ReverseIndexing) + { + mappingDetails += " (Reverse Index)"; + } + } + 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; + } + + /// + /// returns the UmbracoObjectType associated as defined by the supplied relation type, and if reverse indexing has been enabled + /// + /// associated RealationType + /// + 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; + } + + /// + /// Check to see if the content id side of the relation type is valid (doesn't check the node picker id side) + /// + /// Type of the content object (content/ media or member) + /// Type of the relation. + /// + /// true if [is content object type valid] [the specified content object type]; otherwise, false. + /// + 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; + } + + ///// + ///// Insted of rendering the current name of this property, use a consistant label + ///// + ///// + //protected override void Render(HtmlTextWriter writer) + //{ + // writer.WriteLine("
MultiNode Relations
"); + // writer.WriteLine("
"); + // base.Render(writer); + // writer.WriteLine("
"); + //} + } +} diff --git a/components/editorControls/PickerRelations/PickerRelationsDataType.cs b/components/editorControls/PickerRelations/PickerRelationsDataType.cs new file mode 100644 index 0000000000..865c0a27a9 --- /dev/null +++ b/components/editorControls/PickerRelations/PickerRelationsDataType.cs @@ -0,0 +1,88 @@ +using System; +using System.Xml; + +using umbraco.cms.businesslogic.datatype; +using umbraco.interfaces; + +namespace umbraco.editorControls.PickerRelations +{ + /// + /// This datatype will render a CheckBoxList where the options are defined by an XPath expression, + /// + public class PickerRelationsDataType : umbraco.cms.businesslogic.datatype.BaseDataType, IDataType + { + /// + /// + /// + private PickerRelationsPreValueEditor preValueEditor; + + /// + /// + /// + private IDataEditor dataEditor; + + /// + /// + /// + private IData data; + + /// + /// Gets the name of the data type. + /// + /// The name of the data type. + public override string DataTypeName { get { return "Picker Relations"; } } + + /// + /// Gets the id. + /// + /// The id. + public override Guid Id { get { return new Guid(DataTypeGuids.PickerRelationsId); } } + + /// + /// Lazy load the associated PreValueEditor instance, + /// this is constructed supplying 'this' + /// + public override IDataPrevalue PrevalueEditor + { + get + { + if (this.preValueEditor == null) + { + this.preValueEditor = new PickerRelationsPreValueEditor(this); + } + return this.preValueEditor; + } + } + + /// + /// Lazy load the assocated DataEditor, + /// this is constructed supplying the data value stored by the PreValueEditor, and also the configuration settings of the PreValueEditor + /// + public override IDataEditor DataEditor + { + get + { + if (this.dataEditor == null) + { + this.dataEditor = new PickerRelationsDataEditor(this.Data, ((PickerRelationsPreValueEditor)this.PrevalueEditor).Options); + } + return this.dataEditor; + } + } + + /// + /// Lazy load an empty DefaultData object, this is used to pass data between the PreValueEditor and the DataEditor + /// + public override IData Data + { + get + { + if (this.data == null) + { + this.data = new umbraco.cms.businesslogic.datatype.DefaultData(this); + } + return this.data; + } + } + } +} \ No newline at end of file diff --git a/components/editorControls/PickerRelations/PickerRelationsEventHandler.cs b/components/editorControls/PickerRelations/PickerRelationsEventHandler.cs new file mode 100644 index 0000000000..f255d2a99f --- /dev/null +++ b/components/editorControls/PickerRelations/PickerRelationsEventHandler.cs @@ -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 +{ + /// + /// Event handler that will convert a CSV into Relations + /// + public class PickerRelationsEventHandler : ApplicationBase + { + private enum PickerStorageFormat + { + Csv, + Xml + } + + /// + /// Initializes a new instance of PickerRelationsEventHandler, + /// hooks into the after event of saving a Content node, Media item or a Member + /// + 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); + } + + + /// + /// Event after all properties have been saved + /// + /// + /// + 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 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 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 + } + } + } + + /// + /// Clears any existing relations when deleting a node with a PickerRelations datatype + /// + /// The sender. + /// The instance containing the event data. + 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); + } + } + } + + /// + /// Delete all relations using the content node for a given RelationType + /// + /// + /// + /// + /// NOT USED ATM + 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(); + } + } + } + } + + /// + /// + /// + /// + /// id sourced from the Content / Media / Member + /// id sourced from the Picker + /// if true, reverses the parentId and child Id + /// JSON string with id of Picker Relations property instance + 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); + } + } + } +} diff --git a/components/editorControls/PickerRelations/PickerRelationsOptions.cs b/components/editorControls/PickerRelations/PickerRelationsOptions.cs new file mode 100644 index 0000000000..f06221e5d8 --- /dev/null +++ b/components/editorControls/PickerRelations/PickerRelationsOptions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace umbraco.editorControls.PickerRelations +{ + /// + /// Data Class, used to store the configuration options for the PickerRelationsPreValueEditor + /// + internal class PickerRelationsOptions + { + /// + /// Alias of the pickerProperty to get a csv value of IDs from //TODO: a known format for xml fragments would be good too + /// + public string PropertyAlias { get; set; } + + /// + /// The Id of the RelationType to use + /// + public int RelationTypeId { get; set; } + + /// + /// only relevant with parent-child + /// + public bool ReverseIndexing { get; set; } + + /// + /// if true then the property is hidden + /// + public bool HideDataEditor { get; set; } + + /// + /// Initializes an instance of PickerRelationsOptions + /// + public PickerRelationsOptions() + { + // Default values + this.PropertyAlias = string.Empty; + this.RelationTypeId = -1; + this.ReverseIndexing = false; + this.HideDataEditor = false; + } + } +} diff --git a/components/editorControls/PickerRelations/PickerRelationsPreValueEditor.cs b/components/editorControls/PickerRelations/PickerRelationsPreValueEditor.cs new file mode 100644 index 0000000000..93b95b68de --- /dev/null +++ b/components/editorControls/PickerRelations/PickerRelationsPreValueEditor.cs @@ -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 +{ + /// + /// 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 + /// + public class PickerRelationsPreValueEditor : AbstractJsonPrevalueEditor + { + /// + /// 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) + /// + private propertyTypePicker pickerPropertyAliasPicker = new propertyTypePicker(); + + /// + /// RequiredFieldValidator for the ProperyAliasPicker + /// + private RequiredFieldValidator pickerPropertyAliasRequiredFieldValidator = new RequiredFieldValidator(); + + /// + /// drop down list of all relation types + /// + private DropDownList relationTypeDropDownList = new DropDownList(); + + /// + /// RequiredFieldValidator for the RelationType DropDownList + /// + private RequiredFieldValidator relationTypeRequiredFieldValidator = new RequiredFieldValidator(); + + /// + /// 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 + /// + private CheckBox reverseIndexingCheckBox = new CheckBox(); + + /// + /// if selected, then the property on the data editor is hidden (it's only used as a label) + /// + private CheckBox hideDataEditorCheckBox = new CheckBox(); + + /// + /// Data object used to define the configuration status of this PreValueEditor + /// + private PickerRelationsOptions options = null; + + /// + /// Currently selected RelationType + /// + private RelationType relationType = null; + + /// + /// Lazy load the options data object that represents the current state of this datatypes configuration + /// + internal PickerRelationsOptions Options + { + get + { + if (this.options == null) + { + // Deserialize any stored settings for this PreValueEditor instance + this.options = this.GetPreValueOptions(); + + // 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; + } + } + + /// + /// Lazy load currently selected RelationType + /// + 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; + } + } + + /// + /// Initialize a new instance of PickerRelationsPreValueEditor + /// + /// PickerRelationsDataType + public PickerRelationsPreValueEditor(umbraco.cms.businesslogic.datatype.BaseDataType dataType) + : base(dataType, umbraco.cms.businesslogic.datatype.DBTypes.Nvarchar) + { + } + + /// + /// Creates all of the controls and assigns all of their properties + /// + 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); + } + + /// + /// + /// + /// + 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; + } + } + + /// + /// Saves the pre value data to Umbraco + /// + 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); + } + } + + /// + /// Used to remove styling from the built in pickerProperty alias picker DropDownList + /// + /// + 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().OrderBy(x => x.Text).ToArray(); + + this.pickerPropertyAliasPicker.Items.Clear(); + this.pickerPropertyAliasPicker.Items.AddRange(propertyAliasListItems); + } + + /// + /// Replaces the base class writer and instead uses the shared uComponents extension method, to inject consistant markup + /// + /// + 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); + } + } +} diff --git a/components/editorControls/umbraco.editorControls.csproj b/components/editorControls/umbraco.editorControls.csproj index 94d6856f71..02515eaeaa 100644 --- a/components/editorControls/umbraco.editorControls.csproj +++ b/components/editorControls/umbraco.editorControls.csproj @@ -156,6 +156,10 @@ {651E1350-91B6-44B7-BD60-7207006D7003} umbraco.presentation + + {52AB8F1F-FB76-4E8C-885F-0747B6CE71EC} + umbraco.macroRenderings + {6EDD2061-82F2-461B-BB6E-879245A832DE} umbraco.controls @@ -314,6 +318,11 @@ Code + + + + + Code