Sitecore JSS Limit depth of items retrieved in linked fields - sitecore

I have an App Route A for a page and is loading all the fields including a multilist that links to Item App Route B that can have in the multilist a link to Item A, creating a infinite loop. Is there a way to set the depth to only one level?
AppRoute A:
fields:
multifield:
AppRoute B
AppRoute B:
fields:
multifield:
AppRoute A

There are two options, you can create a patch for the configuration file to update the layoutService serializationMaxDepth, but this will affect all the Items:
<configuration>
<sitecore>
<settings>
<layoutService>
<serializationMaxDepth>2</serializationMaxDepth>
</layoutService>
</sitecore>
</configuration>
Or you can craete your custom field serialization resolver.
This class will add the information to the field:
public class CustomFieldSerializer : BaseFieldSerializer
{
public CustomFieldSerializer (IFieldRenderer fieldRenderer)
: base(fieldRenderer)
{
}
protected override void WriteValue(Field field, JsonTextWriter writer)
{
writer.WriteStartObject();
writer.WritePropertyName(field.Name);
writer.WriteValue("Your custom field value here.");
writer.WriteEndObject();
}
}
This class will be the field resolver:
public class GetCustomFieldSerializer : BaseGetFieldSerializer
{
public GetCustomFieldSerializer(IFieldRenderer fieldRenderer)
: base(fieldRenderer)
{
}
protected override void SetResult(GetFieldSerializerPipelineArgs args)
{
Assert.ArgumentNotNull((object)args, nameof(args));
args.Result = new CustomFieldSerializer(this.FieldRenderer);
}
}
And this is the configuration patch to set the resolver class to your field. Make sure to place your config(patch:before) before the field default serialization configuration, in this case the multililist is GetMultilistFieldSerializer you can check this in https://<your-sitecore-domain>/sitecore/admin/showconfig.aspx:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<group groupName="layoutService">
<pipelines>
<getFieldSerializer>
<processor patch:before="processor[#type='Sitecore.LayoutService.Serialization.Pipelines.GetFieldSerializer.GetMultilistFieldSerializer, Sitecore.LayoutService']" type="Foundation.FieldSerializer.GetCustomFieldSerializer, Foundation.LayoutService" resolve="true">
<FieldTypes hint="list">
<fieldType id="1">multilist</fieldType>
</FieldTypes>
</processor>
</getFieldSerializer>
</pipelines>
</group>
</pipelines>
</sitecore>
</configuration>

Related

How can I pass some information in to a Sitecore Webforms for Marketers custom message processor pipeline?

I have a site built using Sitecore 7.5 and Webforms for Marketers 2.5. I am trying to create a custom email message processor pipeline command that will change the TO field for the email before it goes out. But the proper email address needs to come from a Session variable.
Here is my class:
public class CustomEmailMessageProcessor
{
public void Process(ProcessMessageArgs args)
{
//Change the TO address for the email based on the selection in the Subject field
var subjectField = args.Fields.GetEntryByName("Subject");
if (subjectField == null)
{
return;
}
//The value of the selected item will be the ID of a Subject Option
var selectedSubjectOptionItem = Sitecore.Context.Database.GetItem(new ID(subjectField.Value));
if (selectedSubjectOptionItem == null)
{
return;
}
var selectedSubjectOption = selectedSubjectOptionItem.GlassCast<Contact_Us_Subject_Option>();
//Based on the currently selected Region in the Session variable, get the proper
//child of selectedSubjectOption to populate the TO field
???
if (args.To.Length != 0)
{
args.To.Append(",");
}
args.To.Append(proper email address goes here);
}
}
And here is my associated config:
<processMessage>
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="ExpandLinks" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="ExpandTokens" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddHostToItemLink" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddHostToMediaItem" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddAttachments" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="BuildToFromRecipient" />
<!-- Custom setting -->
<processor type="myProject.CustomEmailMessageProcessor, myProject" method="Process" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="SendEmail" />
</processMessage>
The user will select a Subject from a dropdown list on the form. The value of each Subject will be the Guid of a corresponding item. That subject item will have children. Each child will have an email address and will correspond to a Region item in the system. I will then find the child item that matches the Region ID that is stored in Session. Then I will have the correct email address to send the email to.
However I have no idea how to access the Session variable from within the pipeline (or if it is even possible). It doesn't have to be Session. I am happy to pass in the currently selected Region in some other fashion. I just need some way to pass information in that can be accessed from the pipeline code.
Thanks,
Corey
When using the Sitecore WFFM webservice, the save action is run on the CMS, shell site. The save action don't know anything about the user session.
A different more standard solution is create a (custom) Field hidden, and set there the value you can use to find the needed email address.
Create a custom save action and do there your e-mail sending.
Note: If you do not have a good identifier to find the email address watch out with putting an email address or to simple identifier in a (hidden) form value, it can be abused.
Example of a hidden field, based on a <input type="text" .....> for Webforms
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.Form.Web.UI.Controls;
namespace StockpickSitecore.Controls.WFFM.CustomFields
{
public class HiddenEmailfield : SingleLineText
{
private static readonly string baseCssClassName = "scfSingleLineTextBorder";
private string CssClassName { get; set; }
public int MaxLength
{
get
{
return this.textbox.MaxLength;
}
set
{
this.textbox.MaxLength = value;
}
}
public int MinLength { get; set; }
public new string CssClass
{
get
{
return base.CssClass;
}
set
{
base.CssClass = value;
}
}
public HiddenEmailfield()
{
this.Text = "info#yourdomein.com";
}
protected override void OnInit(EventArgs e)
{
this.Attributes.Add("style", "display: none;");
this.textbox.CssClass = "scfSingleLineTextBox";
this.help.CssClass = "scfSingleLineTextUsefulInfo";
this.generalPanel.CssClass = "scfSingleLineGeneralPanel";
this.title.CssClass = "scfSingleLineTextLabel";
this.textbox.TextMode = TextBoxMode.SingleLine;
this.Controls.AddAt(0, (Control) this.generalPanel);
this.Controls.AddAt(0, (Control) this.title);
this.generalPanel.Controls.AddAt(0, (Control) this.help);
this.generalPanel.Controls.AddAt(0, (Control) this.textbox);
}
}
}
Change the constructor HiddenEmailfield and set the this.Text with the Session value.
Creat a item in Sitecore below /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/Custom/
based on Template: /sitecore/templates/Web Forms for Marketers/Field Type
Fill in the Assembly : StockpickSitecore for Example
Fill in the Class: StockpickSitecore.Controls.WFFM.CustomFields.HiddenEmailfield
Now you can use HiddenEmailfield in you webform als field type

How to avoid duplicate name while creating Sitecore item

I'm facing a problem in my Sitecore project. As we are working in a team, we can't track all the item who has created and what name they have given. The problem is, people are creating the item with same name. This is causing some serious problem while moving the items to different environments.
What I want is, while creating the Sitecore item, pipeline method should execute and validate whether its immediate parent already has the same item name.
For example : Parent A has 3 subitems called Child1, Child2, Child3, when developer tried to create a item with name Child2 the popup/alert should display and not to allow him to create the item.
Please help me with this.
You can add your own handler to item:creating event and check if the parent already contains a child with proposed name.
Here is nice post describing how to prevent duplicates items in Sitecore. I've copied the following code from there:
<event name="item:creating">
<handler type="YourNameSpace.PreventDuplicates, YourAssembly" method="OnItemCreating" />
</event>
namespace YourNamespace
{
public class PreventDuplicates
{
public void OnItemCreating(object sender, EventArgs args)
{
using (new SecurityDisabler())
{
ItemCreatingEventArgs arg = Event.ExtractParameter(args, 0) as ItemCreatingEventArgs;
if ((arg != null) && (Sitecore.Context.Site.Name == "shell"))
{
foreach (Item currentItem in arg.Parent.GetChildren())
{
if ((arg.ItemName.Replace(' ', '-').ToLower() == currentItem.Name.ToLower())
&& (arg.ItemId != currentItem.ID))
{
((SitecoreEventArgs)args).Result.Cancel = true;
Sitecore.Context.ClientPage.ClientResponse.Alert
("Name " + currentItem.Name + " is already in use.Please use another name for the page.");
return;
}
}
}
}
}
}
}
I have a blog post out for this which uses the item create / save event and uses index search to identify duplicates. This was implemented and tested with Sitecore 7.2. Here's the config used:
<sitecore>
<events>
<event name="item:creating">
<handler type="MySite.Customizations.Customized_Sitecore.UniqueItemNameValidator, MySite" method="OnItemCreating" />
</event>
<event name="item:saving">
<handler type="MySite.Customizations.Customized_Sitecore.UniqueItemNameValidator, MySite" method="OnItemSaving" />
</event>
</events>
</sitecore>

How to link a droplink to a Treelist in Sitecore

I'm trying to figure out how I can link a Droplink to the selected items in a Treelist.
I have a field Theme, which is the Treelist, and a field MasterTheme, which is the Droplink.
I should be able to select a master-theme in the Droplink, which is filled with the selected data from the Treelist.
I'm pretty new to Sitecore, and I'm not familiar with Custom classes.
You can use the getLookupSourceItems-pipeline for this. With a Droplink you can specify a Sitecore query as source. And with the getLookupSourceItems you can change the source at runtime. The following processor checks the items selected in the Treelist and create a Sitecore query which includes all the items selected in the Treelist.
public class LookupItemsFromField
{
private const string FromFieldParam = "fromfield";
public void Process(GetLookupSourceItemsArgs args)
{
// check if "fromfield" is available in the source
if (!args.Source.Contains(FromFieldParam))
{
return;
}
// get the field
var parameters = Sitecore.Web.WebUtil.ParseUrlParameters(args.Source);
var fieldName = parameters[FromFieldParam];
// set the source to a query with all items from the other field included
var items = args.Item[fieldName].Split('|');
args.Source = this.GetDataSource(items);
}
private string GetDataSource(IList<string> items)
{
if (!items.Any()) return string.Empty;
var query = items.Aggregate(string.Empty, (current, itemId) => current + string.Format(" or ##id='{0}'", itemId));
return string.Format("query://*[{0}]", query.Substring(" or ".Length));
}
}
You have to specify which field is your "Source" field within the Droplink source with fromfield=<SourceField>:
At the end you need to configure this pipeline processor:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<getLookupSourceItems>
<processor patch:before="processor[1]"
type="Website.LookupItemsFromField, Website" />
</getLookupSourceItems>
</pipelines>
</sitecore>
</configuration>
I think this is what you are looking for: http://getfishtank.ca/blog/using-item-field-as-a-data-source-in-sitecore
Basically you will be able to set the datasource of one field to another.

XmlSerializer.Deserialize() behaves different between NECF 2.0 and NETCF 3.5

I am migrating an old NETCF 2.0 Application which uses webservices to NETCF 3.5. The webservice of the foreign server remains the same without changes. Also I newly generated Reference.cs using VS 2008 Command Prompt - and was excited to see the same result as using VS 2005.
My question relates to namespace definition at the root tag by a class which is defined by webservice. The java webservice serializes it with xmlns attribute and XmlSerializer.Deserialize() of NETCF 3.5 blames about this attribute throwing an InvalidOperationException. Using this XML and XmlSerializer.Deserialize() with NETCF 2.0 works as expected. The Object gets deserialized to memory.
Let a few code snippets make things clear.
Exception
InvalidOperationException-There is an error in XML document (2, 2).
InnerException: InvalidOperationException-<InstallDirective xmlns='java:my.foreign.namespace'> was not expected.
at System.Xml.Serialization.XmlSerializer.resolveDeserializingType(XmlReader reader, XmlSerializationReader serialReader, Boolean soap12)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
at System.Xml.Serialization.XmlSerializer.Deserialize(Stream stream)
at My.Neat.Software.Bug.Test()
generated Reference.cs (excerpt)
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="java:my.foreign.namespace")]
public partial class InstallDirective {
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public System.Nullable<System.DateTime> Date;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public JustAnotherThing JustAnotherThing;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public System.Nullable<short> Oid;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Filename;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Version;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Dir;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string Instructions;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Scripts", IsNullable=true)]
public Script[] Scripts;
}
the XML from webservice
<?xml version="1.0" encoding="utf-8"?>
<InstallDirective xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="java:my.foreign.namespace">
<Date>2014-02-11T13:31:49.238+01:00</Date>
<JustAnotherThing>
<FileName>MyFunnyFile.txt</CabFileName>
<Checksum>e759af8bd5787e3f8d62245a7d6aa73d</Checksum>
<FileExists xsi:nil="true" />
<Name>MyFunnyFile</Name>
<Version>1.2</Version>
<Path xsi:nil="true" />
<DependsOn xsi:nil="true" />
<RegistryPath xsi:nil="true" />
</JustAnotherThing>
<Oid>1</Oid>
<Filename>MyFunnyFile.txt</Filename>
<Version>1.2</Version>
<Dir>\My\Path\To\Files</Dir>
<Instructions xsi:nil="true" />
<Scripts xsi:nil="true" />
</InstallDirective>
the code snippet which throws the exception
FileInfo directiveFile = new FileInfo(#"\tmp\install\funnyInstallDirective.xml");
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective));
TextReader reader = new StreamReader(directiveFile.OpenRead());
InstallDirective installDirective = (InstallDirective)xmlSerializer.Deserialize(reader); // BAM!
Thanks for your help!
Indeed the XmlSerializer implementation of .Net Compact Framework 2.0 and 3.5 differs. The solution to this "new" behaviour is easy as well. Because the XmlSerializer which - in my case - serializes XML as a "fragment", we need to declare the XmlRootAttribute. This is due to missing XmlRootAttribute annotation of the type to be serialized within the Reference.cs.
To be backward compatible with XML serialized by NETCF 2.0 implementation, we need to add the namespace by definition of InstallDirective (Reference.cs). Unfortunately I found no programmatically way to get this.
Before:
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective));
FileStream file = File.Create(#"\tmp\install\funnyInstallDirective.xml");
TextWriter writer = new StreamWriter(file);
xmlSerializer.Serialize(writer, installDirective);
After:
XmlRootAttribute att = new XmlRootAttribute(typeof(InstallDirective).Name);
att.Namespace = "java:my.foreign.namespace";
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective), att);
FileStream file = File.Create(#"\tmp\install\funnyInstallDirective.xml");
TextWriter writer = new StreamWriter(file);
xmlSerializer.Serialize(writer, installDirective);
Credits to ta.speot.is who answered the question here.
[UPDATE]
Of course you can beautify it by using the annotated XmlTypeAttribute of the web service type. It will look this way.
XmlTypeAttribute ta = (XmlTypeAttribute)Attribute.GetCustomAttribute(typeof(InstallDirective), typeof(XmlTypeAttribute));
XmlRootAttribute att = new XmlRootAttribute(typeof(InstallDirective).Name);
att.Namespace = ta.Namespace;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InstallDirective), att);

How to patch an attribute value with a Sitecore Include file

I need to create a Sitecore include patch file to add a string to the existing value attribute of the IgnoreUrlPrefixes setting in the web.config.
I have tried to overwriting the default ignored prefixes entirely with the following include file:
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<settings>
<setting name="IgnoreUrlPrefixes">
<patch:attribute name="value">/foo/|/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing</patch:attribute>
</setting>
</settings>
</sitecore>
</configuration>
</settings>
Where /foo/ is the url prefix that I would like to add to the default prefixes. ShowConfig.aspx identifies that the modified configuration has not been applied.
Ideally I would like to be able to simply add /foo/ to whatever exists as the default IgnoreUrlPrefixes values. Does anyone know if this is possible and how to specify it in Sitecore patch syntax?
Good explanation of all possibilities of Sitecore include config files can be found in this John West blog post.
As you can find in the linked post:
patch:attribute: Define or replace the specified attribute.
It does not allow to "add /foo/ to whatever exists as the default IgnoreUrlPrefixes" attribute.
I recently ran into this same issue and it seems like Mark Ursino posted a blog on this particular issue:
http://firebreaksice.com/sitecore-patchable-ignore-lists/
In his example, he executes a custom pipeline after the default Sitecore one to update the value.
So instead, I’ve created a new pipeline processor that comes after the
built-in one (which will support the existing native IgnoreUrlPrefixes
setting) and will allow you to add each path via its own XML config
node. The advantage here is you can patch and continue to patch on
without needing to copy the existing values.
Sample patch file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor type="Sitecore.PatchableIgnoreList.ProcessPatchedIgnores, Sitecore.PatchableIgnoreList"
patch:after="processor[#type='Sitecore.Pipelines.HttpRequest.IgnoreList, Sitecore.Kernel']">
<Paths hint="list:AddPaths">
<foo>/foo</foo>
<bar>/bar</bar>
</Paths>
</processor>
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
Source code for the pipeline processor, from the blog:
using Sitecore.Collections;
using Sitecore.Pipelines.HttpRequest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Sitecore.PatchableIgnoreList
{
public class ProcessPatchedIgnores : HttpRequestProcessor
{
private List<string> _paths = new List<string>();
public override void Process(HttpRequestArgs args)
{
string filePath = args.Url.FilePath;
foreach (string path in _paths)
{
if (filePath.StartsWith(path, StringComparison.OrdinalIgnoreCase))
{
args.AbortPipeline();
return;
}
}
}
public void AddPaths(string path)
{
if (!string.IsNullOrEmpty(path) && !_paths.Contains(path))
{
_paths.Add(path);
}
}
}
}