Search value in body tag in Sitecore - sitecore

I want to implement search functionality, So my requirement is I want to search some keyword in body tag in all content page. I do not know how I can search keyword in body tag in Sitecore. Please guide me?

As Anton outlined, the concept of searching the Body tag is wrong for Sitecore. You want to think in terms of Content in fields of Items. Sitecore's ContentSearch is how you can achieve this.
Sitecore comes with default indexes out-of-the-box that you should use for the search. You should rebuild these via the Index Manager in the Content Editor and then base your search on the basic example I've outlined for you below.
public IEnumerable<Item> Search(string searchterm)
{
string indexName = "sitecore_web_index";
using (var index = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
IQueryable<SearchResultItem> query = index.GetQueryable<SearchResultItem>().Where(i => i.Content.Contains(searchterm)).Filter(predicate);
var searchResults = query.GetResults();
foreach (var hit in searchResults.Hits)
{
yield return hit.Document.GetItem();
}
}
}

jRobbins's answer is sensible (he get's my upvote). However, it is technically possible to index the content of the body tag. I would be cautious with this. I've seen it working well, but I've also seen it completely destroy the performance of a site.
The approach involves the creating a computed field in your index. You populate the computed field by making a web request to your newly published page and scraping the response body tag.
Here's are a couple of module that more or less does that:
https://github.com/efocus-nl/sitecorewebsearch
https://github.com/hermanussen/sitecore-html-crawler
If you can accept something a little less accurate, then you could loop through each of the components on your page and extract content from their datasources. That approach is discussed in this video:
http://www.techphoria414.com/Blog/2012/May/Sitecore_Page_Editor_Unleashed

Related

How to get Zomato restaurant ID using restaurant link?

I want to get details of a restaurant in Zomato. I have it's link as the input (https://www.zomato.com/mumbai/fantasy-the-cake-shop-kalyan?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1). By browsing the documentation of Zomato APIs, I didn't found a way to get it.
I tried searching for the restaurant using search API but it returns many results.
Any help will be appreciated
It's a two-step process:
Find out restaurant's city id, in your case, Mumbai's city id through the /cities API. It's a simple query search.
Use the city id from the above API call in the /search API, like, https://developers.zomato.com/api/v2.1/search?entity_type=city&entity_id=3&q=fantasy%20the%20cake%20shop%20kalyan
This would give all the basic information about a restaurant.
View the page's source and search for window.RES_ID
I had the same issue as you described. This Zomato's API approach is at least odd. It's almost immposible to GET any information about restaurant if you don't know res_id in advance and that's not possible to parse since Zomato will deny access.
This worked for me:
Obtain user-key from Zomato API Credentials (https://developers.zomato.com/api)
Search restaurant url via API (https://developers.zomato.com/api/v2.1/search?entity_id=84&entity_type=city&q=RESTAURANT_URL&category=7%2C9). The more specific you will be, the better results you'll get (This url is specified by city to Prague (ID = 84) and categories Daily menus (ID = 7) and Lunch (ID = 9). If there is possibility do specify city, category or cuisine, it helps, but should't be necessary. Don't forget to define GET method in headers.
Loop or filter through json results and search for the wanted url. You might need to use method valueOf() to search for the same url. Be careful, you might need to add "?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1" at the end of your wanted url so it has the same format. Check that through Zomato API Documentation page.
for (i in body.restaurants) {
var url_wanted = restaurant_url + '?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1'
var url_in_json = body.restaurants[i].restaurant.url;
if (url_wanted.valueOf() == url_in_json.valueOf()) {
var restaurant_id = body.restaurants[i].restaurant.id;
}
console.log('Voala! We have res_id:' + restaurant_id);
}
There you have it. It could be easier though.
Hope it helps!
once you have the url of the rseraunt's page you can simply look for a javascript object attribute named "window.RES_ID" and further use it in the api call.

How to efficiently store records as Ember query parameters

Background: The user can filter a list based on tags, every tag is an Ember Data model. The selected filtering tags should be stored in the URL via query parameters. I also want to display the selected tags in the interface.
What's the best way to go about this?
I'm pretty sure I only want to store the IDs, but if I do this I have to maintain a separate variable / computed property where I store the actual record of the ID which comes from the query parameters. This kind of duplication seems wrong to me.
Apart from this issue I don't know if I should use an array query parameter or build an comma separated string of the IDs. If I do the former I end up with ugly URLs like ?tags=%5B"3"%5D. But doing the later means doing even more work.
So what's your approach to this kind of problem? I hope I'm missing something obvious that doesn't have so many downsides :)
Easy!
Find out which non-alphanumeric characters are not getting encoded in URLs. Using this tool, I found these characters: ~!*()_-.
Pick one character that is not used in your tag names. Let's say it's *. Use it as a delimiter.
Use tag names for tag ids. Or at least enforce unique tag names.
Then you've got a nice readable query:
?tags=foo*bar*baz&search=quux
To implement custom query param serialization/deserialization, do this:
Ember.Route.reopen({
serializeQueryParam: function(value, urlKey, defaultValueType) {
if (defaultValueType === 'array') {
return `${value.join('*')}`;
}
return this._super(...arguments);
},
deserializeQueryParam: function(value, urlKey, defaultValueType) {
if (defaultValueType === 'array') {
return value.split('*');
}
return this._super(...arguments);
}
});
Demo: app, code.
If you must use numeric tag ids in query params (for example, your tag names aren't unique), then you need some more customization: app, code.

How to disable Sitecore WFFM custom fields caching

I have a custom field SomeCustomFieldType in Sitecore WFFM that shows on the left side of the Form Editor a property which is a dropdown with a list of choices.
[VisualProperty("Choose value", 99),
VisualCategory("Custom Properties"),
VisualFieldType(typeof(CountryBasedListChoiceField))]
public string ChosenValue { get; set; }
Instead of using the out-of-the-box ListChoiceField, I wrote my own CountryBasedListChoiceField, which, based on some condition, figures out which choice items need to be displayed in the property dropdown:
public CountryBasedListChoiceField()
: base(((object) HtmlTextWriterTag.Select).ToString())
{
string formID = Sitecore.Context.Request.GetQueryString("formid");
Language formLanguage = Language.Parse(Sitecore.Context.Request.GetQueryString("la"));
Item formItem = Sitecore.Context.ContentDatabase.GetItem(formID, formLanguage);
string countryName = SitecoreUtility.GetCurrentCountryISOCode(formItem, formLanguage);
string choicesRootPath = CountryConfigurationManager.GetCountrySite(countryName).Metadata.FormsStylesheetsItemPath;
Item choicesRootItem = Sitecore.Context.ContentDatabase.GetItem(choicesRootPath, formLanguage);
ChoicesRoot = choicesRootItem.ID.ToString();
}
It really doesn't matter how the above code works, since it works just fine.
The problem I am having is that the code above is executed only once, the very first time that the user opens a form in the form editor and clicks on a field that is of type SomeCustomFieldType.
After that, the list of choices to be displayed must be cached because this code is never being hit anymore. Even closing the browser and reopening it is not enough to clear that cache -- iisreset obviously clears that cache.
I would like to know if there is a way to stop this caching from happening.
Thanks in advance!
Firstly, you should check to ensure that it the rendering is not cached found here: '/sitecore/layout/Renderings/Modules/Web Forms for Marketers/Form' (the version of WFFM I have open right now it is NOT the default). Also check to ensure that any renderings it is contained within are also not cached.
Looking at the forms code, the form item and field containers are constructed every time. I also checked the .ascx that is called and it has no default output cache. You can start by looking deeper in Sitecore.Forms.Core.dll at the class 'Sitecore.Form.Web.UI.Controls.SitecoreSimpleFormAscx', but I would try the simple first.

Using cookies to filter data in Google Analytics

I am trying to filter Google Analytics data for my company site based on a cookie. I don't want to track internal traffic, but I can't just filter based on an IP address range because there are some internal users who we want to still track. I have some pretty simple code for adding a cookie, but I am just not sure where to add the code. I am really new to cookies, and couldn't find anything online that was clear on how to actually add or use the cookie.
<html>
<head>
<title>Remove My Internal Traffic from Google Analytics</title>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-YY']);
_gaq.push(['_setVar','employee']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
So my question is, where does this code actually go? Thanks for helping out my novice skills with cookies.
Do not use setVar (this is deprecated), use _setCustomVar:
_setCustomVar(index, name, value, opt_scope)
The call goes before the _trackPageview Call.
There are five custom vars in standard GA (50 in premium), that's "index". 'Name' and 'value' should be clear.
CustomVars are either valid for the current page, for the session or for the visitor (in the last case they are valid until the visitors clears the cookies in his browsers unless he waits six months before he visits you site again).
Like every instruction with the asynonchronous GA code this is "pushed" on the gaq-Array, so the correct call would be:
_gaq.push(['_setCustomVar',
1, // This custom var is set to slot #1. Required parameter.
'Items Removed', // The name acts as a kind of category for the user activity. Required parameter.
'Yes', // This value of the custom variable. Required parameter.
2 // Sets the scope to session-level. Optional parameter.
]);
which is taken from the Google documentation here:
https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables#setup.
I still maintain that for your use case the opt-out plugin is the better solution.
UPDATE: Thinking about it I don't think you need setCustomVar or custom cookies at all. Have your employees go to your website via a link like:
mywebsite.com?utm_source=allyourbasearebelongtous
Then go to the profile settings and create a custom filter, set to exclude, filter field "campaign source" , filter pattern "allyourbasearebelongtous" (or whatever name you gave to your campaign parameter).
This uses also a cookie (the standard google cookie) but does not need any custom code at all. The campaign source parameter is valid until they visit another campaign geared towards your site, so if somebody wants to test the GA code they need to delete their cookies or use incognito mode (but that't not different from setting a custom cookie or setCustomVar-methods).

.Net MVC route for undetermined number of folders (nested category structure)

I'm exploring the possibility of using MVC for my next e-commerce site. One thing I can't seem to figure out is whether or not I can use the same URL convention I normally use. Currently, the URL for any product could be one of the following:
Category/SubCategory/Product1.html
Category/SubCategory/SubSubCategory/Product2.html
Category/SubCategory/SubSubCategory/Product3.html
Category/SubCategory/SubSubCategory/SubSubSubCategory/Product4.html
etc.
The issue I'm having is with the nested category structure. So far the only thing I've come up with is as follows:
routes.MapRoute(
"Products",
"{categories}/{productname}",
new { controller = "Product", action = "Details", productname = UrlParameter.Optional },
new { categories = #"\w+/\w+" }
);
I was hoping that {categories} could be matched with any one of the following which I could process to identify the right category that the product belongs to:
Sport/Tennis/Rackets/ProductA
Sport/Badminton/Rackets/ProductB
But the route shown above doesn't work correctly.
Does anyone know how this can be achieved, or if it can't be done?
The routing system allows you to define catchall parameters, which ignore slashes and capture
everything up to the end of a URL. Designate a parameter as being catchall by prefixing it with an
asterisk (*).
routes.MapRoute(null, "Articles/{*articlePath}",
new { controller = "Articles", action = "Show" }
);
You can only have one catchall parameter in a URL pattern, and it must be the last (i.e.,
rightmost) thing in the URL, since it captures the entire URL path from that point onward.
One Caveat though, it doesn’t capture anything from the query string as route objects only look at the
path portion of a URL.
Catchall parameters are useful if you’re letting visitors navigate through some kind of arbitrary
depth hierarchy, such as in a content management system (CMS).
You can use the RouteData object to extract information about the route. For your needs, you would probably create a custom route handler that parses the route data and calls the correct controller methods.
You need access to the individual segments of the URL so you need to divide the category segment into two segments. That would make it much easier.
Let's say we call Tennis and Badminton categories and Rackets within those categories as a product class
You need a way to access the category, productClass and productName parameters. Supposing that "Sport" is fixed in this case, I will do it like this:
routes.MapRoute(
"Products",
"sport/{category}/{productClass}/{productName}",
new { controller = "Product", action = "Details", productClass = UrlParameter.Optional, productName = UrlParameter.Optional }
);
Your action method will be something like this
public ActionResult Details(string category, string productClass, string productName){
//Do whatever you need to do in order to get the specified product
}
You could use Areas in MVC2
So it would read:
Area/Controller/View/id
So in your case it would end up being:
Sport being the area,
Tennis The controller,
Rackets the view,
ProductA being an ID or querystring,
http://www.asp.net/mvc/videos/aspnet-mvc-2-areas
Hope this makes sense.