(I'm using Sitecore 8.1 Update 3 with Glass Mapper.Sc version 4.1.1.66)
I've run into an issue where some of the properties of a Sitecore item are not being populated in code through Glass Mapper. The values in our Content Base item (Id, Name, Display Name, typical Sitecore fields) seem to be getting populated correctly, but the child item's fields (I'll call it Overview) aren't mapped at all. They're all strings but they all end up null, even though the Content Base's values look to be correct. We also have child class maps working in other areas of the same, so that may not be the cause here.
Earlier in this project, we had an issue with Glass Mapper where field names that included spaces were not being read. However, I've made sure to leave out any spaces in field names, but this doesn't solve the issue.
Another possible contributor to the issue is that we have multiple languages on the site, so it's conceivable that language fallback may be complicating things. However, we have fallback enabled and working properly across the site without issues.
I can post code if needed, but for the most part, it's just POCO's and mapping classes.
Any ideas on what other parts I should be looking into?
You need to use the VersionCountDisabler(), you can find more details about it in this previous question and this blog post.
The use of the disabler is not supposed to be required on Sitecore 8.1 and when using Language Fallback, but I can confirm that this is an issue and using the VersionCountDisabler() solved the issue for us.
Internally Glass will check if a version of an item exists, if not it returns null. It seems that Sitecore will return a version count for Items with Layout, but not for datasource Items.
We have wired out the disabler slightly different than using the global.asax file, instead patching into the http request pipelines:
using Glass.Mapper.Sc;
using Sitecore.Pipelines.HttpRequest;
namespace MyProject.Custom.Pipelines.HttpRequest
{
public class ItemVersionCountDisablerBegin : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
Sitecore.Context.Items["Glass::VersionCountDisabler"] = new VersionCountDisabler();
}
}
public class ItemVersionCountDisablerEnd : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
VersionCountDisabler disabler = Sitecore.Context.Items["Glass::VersionCountDisabler"] as VersionCountDisabler;
Sitecore.Context.Items["Glass::VersionCountDisabler"] = null;
disabler?.Dispose();
}
}
}
And then patch this into http request begin and end pipelines:
<pipelines>
<httpRequestBegin>
<processor type="MyProject.Custom.Pipelines.HttpRequest.ItemVersionCountDisablerBegin, MyProject.Custom" patch:before="*[1]"/>
</httpRequestBegin>
<httpRequestEnd>
<processor type="MyProject.Custom.Pipelines.HttpRequest.ItemVersionCountDisablerEnd, MyProject.Custom" />
</httpRequestEnd>
</pipelines>
After some experimentation, I found the cause of the issue.
In my mapping class, you'll see this line:
config.Field(f => f.HeaderPrefix).FieldName("AssetOverviewContentHeaderPrefix");
The field name seems kinda long, right? We were prepending the category name onto the field name in order to avoid any duplicate names in items since we do a fair amount of inheritance.
That's where the problem lies. When I removed the "AssetOverviewContent" from the field names, everything worked fine. Based on this, I did some more experimentation.
I found that field names up to 23 characters long worked just fine. 24 or more and they won't map. I have no idea why this number in particular is the limit, but my guess is that there's some other mapping going on somewhere that's hitting a limit.
A little more experimentation also found that mapping using FieldId also doesn't work. Guids are going to be more than 23 characters long, so that makes some sense. However, you can't really do a guid in less than 23 characters so I can't confirm.
I may end up looking at the Glass Mapper code sometime soon to see if I can track down the answer. But now that I know there's a problem, I can avoid it.
Related
I have a page that inserts some predefined components in the placeholders, when user creat it (like gallery and header for example).
I also want web masters to give a freedom to insert other possible components to the placeholders around the predefined components, but I want to prohibit somehow to edit/delete the predefined components (gallery and header) from the page.
However, a content editor can add another gallery component (technically possible) to the same page, and delete it if he wants to. But predefined components should just not be touched (at least be removed from the placeholders. In other words - I don't want to have a delete button in the Experience editor next to the predefined components).
There is a way to fully prohibit editing the component rendering, but I still want web masters to insert components by themselves if they want. I just want them to do not modify the components that come when page is created.
As I understand this can be done somethere on the RenderingReference level, but I cannot figure it out how. Could you give a tip please?
Okay, I have found a way to do this.
Just in case if somebody will need it.
First, we need to create a Processor to catche page renderings in the Experience Editor.
public class CheckLockedComponentsHandler : GetClientActionsChromeData
{
public new void Process(GetChromeDataArgs args)
{
if (args.ChromeType == "rendering")
{
if (args.CustomData["renderingReference"] is RenderingReference rf && !string.IsNullOrEmpty(rf.Settings.Parameters))
{
var rmParams = WebUtil.ParseUrlParameters(rf.Settings.Parameters);
args.ChromeData.Custom["editable"] = string.IsNullOrEmpty(rmParams["editable"]) ? "true" : rmParams["editable"];
}
}
}
}
When, we add a property editable = false when creating a locked component.
And finally we need to register our processor in the configuration
<pipelines>
<getChromeData>
<processor type="CheckLockedComponentsHandler,MyLib" patch:after="*[last()]"/>
</processor>
<getChromeData>
</pipelines>
I have a form here with a nested table - where each table can dynamically grow, i.e., the inner table (w/ Transit No and Account No) and the outer table (Accounts by ID No). Here is an example:
(Behind the buttons:
Add - $.parent.tbl.Row.instanceManager.addInstance();
Remove - $.parent.instanceManager.removeInstance(this.parent.index); (In
production I make sure there is at least one row to remove...)
In the definition for each table I do not have checked 'Repeat Table for Each Data Item'. This works great. However I did try with that checked and the outcome was the same.
Now, when I email the form and open the attachment, this is what I see:
You can see that the second table didn't make it, and apparently a row was added to the inner table in the first, without any data.
Any ideas on what's going wrong here? And what I can do about it?
Unfortunately I'm not sure what's wrong with your form but I have made a similar form that works - so I can show you how I did it and list a few things that I can think of that can cause problems.
This is what my form looks like and when I e-mail it, it comes out exactly the way it is:
(It has repeatable parent- and childsubforms like yours)
I did it entirely with JS though, no FormCalc and Dollar $igns :D
When a button is pressed I call a function from a Scriptobject.
These are the main parts of my script inside my functions:
Adding a Subform:
var oNewInstance = subform.instanceManager.addInstance(1);
Deleting a Subform:
if (subform.instanceManager.count > subform.instanceManager.occur.min)
{
subform.instanceManager.removeInstance(subform.index);
}
And these are my subforms' properties (in German, but you can figure it out :P):
Your problem might also have completely other reasons though, make sure you don't have any changes in an initialize,docReady, preSubmit and similar actions that occur between sending and opening the sent PDF.
Also before sending it as an e-mail you have to save it in Acrobat as a Reader Extended PDF:
Besides that I've noticed that sometimes problems can occur due to the target version (Selectable in LCD under File > Form Properties > Defaults).
It helped me sometimes to set it to the newest one.
This question already has answers here:
How to generate custom unique ID
(2 answers)
Closed 8 years ago.
I am working on sitecore multisites . I have multiple websites ex test1, test2, test3, test4. All are configured in webconfig. Means test1, test2 ,test3 and so on . So URL //test1 will always point to there local home folder which will be inside of test1/home.
But as per my requirement i have one global folder which are separate from all websites but pages inside of this will be common for all websites.
Ex:
sitecore/Root/Global/Category/A
sitecore/Root/test1
sitecore/Root/test2
sitecore/Root/test3
Now i am not able to get page A if i am in //test1 , and want to access page A and URL should be to //test1/Category/A.
Please help.
I'm not usually a fan of re-posting an exact answer that I posted previously, especially when I posted the original barely over a week ago, but the following is from this post.
Every page that is managed in Sitecore is a Sitecore Item. As such, you should be able to just navigate to the name of the player item. If you were trying to say in your post that category items are stored in globals and not as pages, then you are left with the following options:
Query String: test1/Category?categoryId={ID of Category}
If this is the route that you choose to take then I would suggest using the category item's Sitecore ID for the value of the query string parameter.
If you have other IDs then put the specified ID there, however it would be easiest with Sitecore IDs
What you would then do is get the item with the ID specified in the query string parameter (or get the item with the category ID specified in the query string parameter, depending on which route you take) and display the data for that category on the page
Sitecore.Context.Database.GetItem(Request.QueryString["categoryId"])
categoryItems.FirstOrDefault(categoryItem => categoryItem["Category ID"] == Request.QueryString["categoryId"])
Note that this assumes that the Category ID is a field, not the Sitecore ID
If it is the Sitecore ID then change the lambda to use categoryItem.ID == new ID(Request.QueryString["categoryId"]
Regardless of which one you use, I suggest adding null checks to the QueryString getter
Sublayout Parameters
If you use this method, the query string will not change and people will not be able to get to the page from the direct URL
The process is the same as for query strings, except that you are using Sublayout parameters instead
Note that you must set these in a parent sublayout or in Sitecore (which means that you have a separate page for each player - i.e. this would be a bad solution)
Virtual Items
This is really what I think you are looking for
This can be a lot of work if you do it from scratch, or you can use the Wildcard Module
If you do it from scratch, you will need a custom pipeline and/or processor for handling the requests
If the Wildcard Module isn't going to work, and if this is the case for all sites in your Sitecroe instance, you could write a custom item resolver and insert it in the httpBeginRequest pipeline right after the built in item resolver.
This is sort of from memory, but should get you started:
namespace Example
{
public class CategoryItemRewsolver : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
if (Sitecore.Context.Item != null) return; // Item has already been resolved
if (args.Context.Request.Path.StartsWith("Category"))
{
Sitecore.Context.Item = Sitecore.Context.Database.GetItem("sitecore/Root/Global" + args.Context.Request.Path);
}
}
}
}
You'll obviously want to replace paths with your own (And possibly allow them to be configured through the .config file).
Then patch this into the pipeline:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<httpRequestBegin>
<processor patch:after="*[#type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" type="Example.CategoryItemResolver,Example" />
</httpRequestBegin>
</pipelines>
</sitecore>
</configuration>
I am currently in the process of writing a custom DataProvider. Using the Intergrate External Data documentation.
I've managed to show the external data in the Sitecore back end. However whenever I try to view the data in the items I created, I am getting an error
Null ids are not allowed. <br> Parameter name: displayName
There seems to be precious little on the subject on how to create a custom DataProvider on the Sitecore Developer Network.
The example on their website seems to only show how to import a SINGLE item into a static database. However I am simply trying to merge some items into the hierarchy and I can't find any useful documentation.
It seems that one of your methods that should return an ID doesn't. It might be GetChildIds and/or GetParentId.
Nick Wesselman wrote a good article about it gathering all the information including an example on the Marketplace. I think that is your best start. You can read it here.
Turns out I needed to include at the very least, the Fields->Section->Template in the GetParent method. To be on the safe side I included the Fields/Sections/Templates into my implementations of
GetChildIDs
GetItemDefinition
GetParentID
It wasn't obvious that this was the case, since I had in fact implemented the GetTemplates method correctly, and I had expected that should be enough.
I've been trying to programmatically reproduce the behavior of editing a Content Type's field properties in the SharePoint site management screen and selecting the "Required" radio button with no sucess using the WSS 3.0 web service's Webs.asmx "UpdateContentType" method.
The first difficulty was the issue with the MSDN documentation that said fields should be of a FieldRef type when in fact they need to be of a Field type (reference). Adding fields and deleting fields works fine after the fix, but updating fields seems to not function at all. (It should also be noted that I followed the recommendation on the previous link to use Method="2" for updating fields but it changes nothing, and using Method values other than 1 for adding or other than 3 for deleting also function correctly).
Here's the web service call (slightly modified with strings instead of XmlNode objects for readability):
SharePointWebServices.Webs webService = new SharePointWebServices.Webs();
webService.Url = "http://mysharepointserver/site";
webService.UseDefaultCredentials = true;
webService.UpdateContentType(
#"0x01005A089D9EC8A382458FB1F6C72096D52A",
#"<ContentType />",
#"<Fields />",
#"<Fields><Method ID=""1""><Field Name=""SomeField"" ID=""{8a4803c4-6545-4a7a-804d-237eebff0ce3}"" Required=""TRUE"" Hidden=""FALSE"" ReadOnly=""FALSE"" PITarget="""" PIAttribute="""" PrimaryPIAttribute="""" Aggregation="""" Node="""" /></Method></Fields>",
#"<Fields />");
After the call, the field is still Required="FALSE".
A quick look into the stssoap.dll assembly indicates that the "Required" property is apparently ignored during the update process. Is this normal behavior? If so, what is the recommended method for programmatically changing the "Required" field from client code (not executing on the SharePoint server)?
Any help would be greatly appreciated.
I've investigated this and found the same thing. I also tried adding the attribute Cmd="Update" to the Method element without success. This example of how to use UpdateContentType was helpful too.
I don't believe you will be able to do this with the out-of-the-box SharePoint services. You've verified from looking at stssoap.dll that this doesn't appear to work correctly. Another 'client'-style option is to use RPC methods but none appear to provide functionality for content types at all.
The web services are particularly frustrating because this type of not-so-edge case regularly comes up. It is very rare that I consider using them because of the time wasting involved with their limitations.
Do you have any option of deploying custom code to the server? You could develop this functionality using the object model and wrap it in your own custom web service (there is a walkthrough here) quite easily.
Here is an example adapted from Gabe Wishnie that does what you require:
SPContentType myContentType = myWeb.ContentTypes["myContentType"];
string internalName = myContentType.Fields["fieldToUpdate"].InternalName;
myContentType.FieldLinks[internalName].Required = false;
myContentType.Update(true);
Sorry this isn't more helpful but it's a common story when using the WSS 3.0 / SharePoint 2007 web services.