Sitecore multisites website URL routing [duplicate] - sitecore

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>

Related

How can I reuse a parameterized Postman test across endpoints?

In Postman, I can create a set of common tests that run after every endpoint in the collection/folder Tests tab, like so:
pm.test("status code is 200", function () {
pm.response.to.have.status(200);
});
But how should I do this for my schema validation on the response object? Each endpoint has a different expected schema. So I have something like this on each individual endpoint:
const schema = { type: 'array', items: ... }
pm.test('response has correct schema', function () {
const {data} = pm.response.json();
pm.expect(tv4.validate(data, schema)).to.be.true;
});
I can't extract this up to the collection level because each schema is different.
Now I find that I want to tweak that test a little bit, but I'll have to copy-and-paste it into 50 endpoints.
What's the recommended pattern here for sharing a test across endpoints?
I had the same issue some years ago, and I found two ways to solve this:
Create a folder for each structure object
You can group all your test cases that share the same structure into a new folder and create a test case for this group. The issue with this is that you will be repeating the requests in other folders. (For this solution, you will need to put your "tests cases" into the folder level)
Create a validation using regular expressions (recommended)
Specify in the name of each request a set of rules (these rules will indicate what kind of structure or call they might have). Then you create a validation in the first parent folder for each type of variation (using regex). (You will need to create documentation and some if statements in your parent folder)
E.g.: [POST] CRLTHM Create a group of homes
Where each initial is meaning to:
CR: Response must be 201
LT: The response must be a list of items
HM: The type of the response must be a home object
And the regex conditional must be something like this (this is an example, please try to make your regex accurate):
if(/CRLTHM\s/.test(pm.info.requestName))
(In this image, NA is referring just to Not Authenticated)

Sitecore to prevent removing component

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>

Glass Mapper not mapping some item properties

(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.

Sitecore 8 - Extend the Multilist with Search controller

We have two problems with 'Multilist with Search' controller.
How do we change the display field?
Currently it shows something like below.
136330 (City item - Cities) we want to display the Display name + Language
is it possible?
We have 4 different languages so, we need to filter it by the language as well. We found some resources which says that we can override it by implementing Sitecore.Buckets.FieldTypes.BucketList but we were not able to found a proper solution for these questions.
We already tried the below link but it only works on load but when we search a value the result is as mention in point 1.
How to get a Multilist with Search field to not display referenced items' version and language?
Waiting for a good answer?
Found the answers and just override the OutputString method as below for 1.
public override string OutputString(Item item)
{
return string.Format("{0} - {1}", (object)item.DisplayName,item.Fields["Postal code"].ToString());
}
And for 2 used the following code in the DoRender method.
using (new LanguageSwitcher(Sitecore.Context.Language))
sources = LookupSources.GetItems(current, this.Source);

REST URIs and operations on an object that can be commented on, tagged, rated, etc

I'm doing research into a web API for my company, and it's starting to look like we might implement a RESTful one. I've read a couple of books about this now (O'Reilly's "RESTful web services" seeming the most useful) and have come up with the following set of URIs and operations for an object that can be commented on, tagged, and rated.
It doesn't really matter what the object is, as this scenario applies to many things on the net, but for the sake of argument lets say it's a movie.
Some of these seem to fit quite naturally, but others seem a bit forced (rating and tagging particularly) so does anybody have any suggestions about how these could be improved? I'll list them with the URI and then the supported verbs, and what I propose they would do.
/movies
GET = List movies
/movies/5
GET = Get movie 5
/movies/5/comments
GET = List comments on movie 5
POST = Create a new comment on movie 5
/movies/5/comments/8
GET = Get comment 8 on movie 5
POST = Reply to comment 8 on movie 5
PUT = Update comment 8 on movie 5
/movies/5/comments/8/flag
GET = Check whether the movies is flagged as inappropriate (404 if not)
PUT = Flag movie as inappropriate
/movies/5/rating
GET = Get the rating of the movie
POST = Add the user rating of the movie to the overall rating
Edit: My intention is that the movie object would contain its rating as a property, so I wouldn't really expect the GET method to be used here. The URI really exists so that the rating can be an individual resource that can be updated using the POST verb. I'm not sure if this is the best way of doing it, but I can't think of a better one
/movies/5/tags/tagname
GET = Check whether the movies is tagged with tagname (404 if not; but if it is tagged with the tag name should it return the actual tag resource by redirecting to something like /tags/tagname?)
PUT = Add tag tagname to the movie, creating the tag resource /tags/tagname if required
DELETE = Remove tag tagname from the movie, deleting the tag resource tags/tagname if nothing is tagged with it after this removal
Note that these wouldn't be the entire URIs, for example the URI to list the movies would support filtering, paging and sorting. For this I was planning on something like:
/movies/action;90s/rating,desc/20-40
Where:
action;90s is a semi-colon delimited set of filter criteria
rating,desc is the sort order and direction
20-40 is the range of item indices to get
Any comments about this API scheme too?
Edit #1
This post is getting quite long now! After reading some of the answers and comments, this is the changes from above I'm planning on making:
Tags will be handled as a group rather than individually, so they will be at:
/movies/5/tags
GET = List tags
POST = Union of specified tags and existing tags
PUT = Replace any current tags with specified tags
DELETE = Delete all tags
I'm still really not sure how to handle flagging a comment though. One option is that instead of POSTing to a comment replying to it, a comment object will include its parent so it can be POSTed to the general URI, i.e.
/movie/5/comment
POST = Create a new comment (which may be a reply to a comment)
I could then use the POST to a comment to flag it. But this still doesn't feel quite right.
/movie/5/comment/8
POST = Flag comment
Most of what you have looks good. There were just a couple of strange things I saw. When I put my URLs together, I try to follow these four principles.
Peel the onion
If you make the R in REST really be a resource then the resource URL should be able to be peeled back and still be meaningful. If it doesn't make sense you should rethink how to organize the resource. So in the case below, each makes sense. I am either looking at a specific item, or a collection of items.
/movies/horror/10/
/movies/horror/
/movies/
The following seems funny to me because flag isn't a resource, it's a property of the movie.
/movies/5/comments/8/flag -> Funny
/movies/5/comments/8/ -> Gives me all properties of comment including flag
Define the View
The last peice of the URL describes how to show the resource. The URL /movies/horror/ tells me I will have a collection of movies refined by horror. But there might be different ways I want to display that collection.
/movies/horror/simple
/movies/horror/expanded
The simple view might just be the title and an image. The expanded view would give a lot more information like description, synopsis, and ratings.
Helpers
After the resource has been limited and the proper view figured out, query string parameters are used to help the UI with the little stuff. The most common query string parameters I use are
p => Page
n => number of items to display
sortby => field to sort by
asc => sort ascending
So I could end up with a URL like
/movies/horror/default?p=12&n=50&sortby=name
This will give me the list of movies limited to horror movies with the default view; starting on page 12 with 50 movies per page where the movies are sorted by name.
Actions
The last thing needed are your action on the resource. The action are either collection based or item based.
/movies/horror/
GET -> Get resources as a list
POST -> Create, Update
/movies/horror/10/
GET -> Get resource as item
POST -> Update
I hope this helps.
I disagree with the edit. Queries should be defined by querystrings as per Martijn Laarman's post. i.e.:
/movies?genre=action&timeframe=90s&lbound=20&ubound=40&order=desc
Well, the way I see it some of the information you return now as objects could simply be added to the metadata of its parent object.
For instance, rating could be part of the response of /movies/5
<movie>
<title>..</title>
..
<rating url="movies/ratings/4">4</rating>
<tags>
<tag url="movies/tags/creative">creative</tag>
...
Removing a tag simply means posting the above response without that tag.
Also queries should go in URL variables, I believe:
/movies/?startsWith=Forrest%20G&orderBy=DateAdded
Based on my understanding of ROA (I'm only on chapter five of RESTful Web Services) it looks good to me.
This is an awesome initial draft for a spec of a REST API. The next step would to specify expected return codes (like you did with "404 No Tag Available"), acceptable Content-Types, and available content-types (e.g., HTML, JSON). Doing that should expose any additional chinks you'll need to hammer out.
#Nelson LaQuet:
Using the HTTP methods as they are actually defined gives you the safety of knowing that executing a GET on anything on a web site or service won't eat your data or otherwise mangle it. As an example (pointed out in RESTful Web Services) Google's Web Accelerator expects this behaviour -- as stated in the FAQ -- and presumably other services do too.
Also it gets you idempotency for free. That is doing a GET, DELETE, HEAD or PUT on a resource more than once is the same as doing it only once. Thus if your request fails then all you have to do is run it again.
This is not REST.
A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC's functional coupling].
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven