Multiple TemplateIds not working in Sitecore's Advanced Database Crawler - sitecore

I have a "Featured" widget to lead visitors to items I want to feature on certain pages. So I'm trying to get Alex Shyba's Advanced Database Crawler for Sitecore to return all the items that refer to the context item. If I put in one template ID, it works fine. But if I pipe delimit the two templates, I never get a result. What am I doing wrong?
var searchParam = new MultiFieldSearchParam()
{
Database = Sitecore.Context.Database.Name,
Language = Sitecore.Context.Language.Name,
TemplateIds = "{E5B41848-3C07-4F17-84A5-C2C29AD43CAE}|{0C2E35D7-C4C9-478B-B4AB-DE8C2A00908B}"
};
var refinements = new List<MultiFieldSearchParam.Refinement>();
refinements.Add(new MultiFieldSearchParam.Refinement("pages", contextItemGUID));
searchParam.Refinements = refinements;
var runner = new QueryRunner("web");
foreach (var skinnyItem in runner.GetItems(searchParam))
{
yield return skinnyItem.GetItem();
}
Again, if I make that TemplateIds a single GUID (either one), it works as expected, but just returning, obviously, items of the specified template.

As Mark notes, it's a bug in the ADC. Our solution was to refactor the ApplyTemplateFilter method as follows:
protected void ApplyTemplateFilter(CombinedQuery query, string templateIds, QueryOccurance occurance)
{
ApplyIdFilter(query, BuiltinFields.Template, templateIds, occurance);
}

Related

Sharepoint 2013: Create a script for updating a list view when i adda new field

I have several lists created with several different views each one, it is asked to me to create a sript that allows this: everytime a new field is added on the list, this field must be automatically be added to all the views that already exists. I need a script because there are so many lists, and so many views that its tedieus to do it one view by one. Is there anyway to do this programatically?
You could create an event receiver for the lists(suppose custom list), then add filed to all views.
Sample code:
SPWeb web = site.OpenWeb();
SPList list = web.Lists["TestListLee"];
var tmpViews = new List<SPView>();
foreach (SPView view in list.Views)
{
if (!view.ViewFields.Exists("myField"))
{
view.ViewFields.Add("myField");
tmpViews.Add(view);
}
}
for (int i = 0; i < tmpViews.Count; i++)
{
tmpViews[i].Update();
}

Autoroute Bulk operations in Orchard

If you customize an autoroute part you have the option to recreate the url on each save.
The help text under this option says:
"Automatically regenerate when editing content
This option will cause the Url to automatically be regenerated when you edit existing content and publish it again, otherwise it will always keep the old route, or you have to perform bulk update in the Autoroute admin."
I have digged all around but I cannot find anywhere an "Autoroute admin".
Is it really there?
It was a proposed feature never implemented?
Any idea to do a bulk update even without an Admin page?
Thanks
EDIT after #joshb suggestion...
I have tried to implement a bulk operation in my controller.
var MyContents = _contentManager.Query<MyContentPart, MyContentPartRecord>().List().ToList();
foreach (var MyContent in MyContents) {
var autoroutePart = recipe.ContentItem.As<AutoroutePart>();
autoroutePart.UseCustomPattern = false;
autoroutePart.DisplayAlias = _autorouteService.GenerateAlias(autoroutePart);
_contentManager.Publish(autoroutePart.ContentItem);
}
In this way it recreates all aliases for the types that contain the given part MyContentPart.
With some more work this code can be encapsulated in a command or in a new tab in Alias UI.
After finished the current project I'm doing I will try that...
You could create a module and implement a command that does a bulk update. Shouldn't be too much work if you're comfortable creating modules. You'll need to implement DefaultOrchardCommandHandler and inject IContentManager to get all the parts you're interested in.
Enable Alias UI in the modules section will give you the admin section for managing routes, however I'm not sure what kind of bulk updates it offers
Publishing the ContentItem will do nothing if it is already Published (as it was in my case).
Instead, one could call the PublishAlias method on the AutorouteService. I ended up with a Controller, something like this:
using Orchard;
using Orchard.Autoroute.Models;
using Orchard.Autoroute.Services;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Security;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace MyNamespace.MyModule.Controllers {
public class AutorouteBulkUpdateController : Controller {
private readonly IOrchardServices _orchardServices;
private readonly IAutorouteService _autorouteService;
private Localizer T { get; set; }
public AutorouteBulkUpdateController(IOrchardServices orchardServices, IAutorouteService autorouteService) {
_orchardServices = orchardServices;
_autorouteService = autorouteService;
T = NullLocalizer.Instance;
}
public ActionResult Index() {
if (!_orchardServices.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings"))) {
return new HttpUnauthorizedResult();
}
int count = 0;
IEnumerable<AutoroutePart> contents;
do {
//contents = _orchardServices.ContentManager.Query<AutoroutePart>(VersionOptions.Latest, new string[] { "Page" }).Slice(count * 100, 100).ToList();
contents = _orchardServices.ContentManager.Query<AutoroutePart>(VersionOptions.Latest).Slice(count * 100, 100).ToList();
foreach (var autoroutePart in contents) {
var alias = _autorouteService.GenerateAlias(autoroutePart);
if (autoroutePart.DisplayAlias != alias) {
autoroutePart.UseCustomPattern = false;
autoroutePart.DisplayAlias = alias;
_autorouteService.PublishAlias(autoroutePart);
}
}
_orchardServices.TransactionManager.RequireNew();
_orchardServices.ContentManager.Clear();
count += 1;
} while (contents.Any());
return null;
}
}
}

Set queryable source on Rendering Parameter Template field

I have a Rendering Parameter template applied to a sublayout. It has a single Droptree field on it, and I want to set the Source of that field to a Sitecore query so I can limit the options available for that field.
Source can be:
query:./*
or
query:./ancestor-or-self::*[##templatename='MyTemplate']/
The query just needs to grab items relative to the content item that we're on. This normally works with Droptree fields in the content editor.
However I'm finding that the query isn't working here because we're in the rendering parameters, so it's not using the content item as it's context.
The query fails and I just get the full Sitecore tree.
I found this can be fixed up for the Datasource field with 'Queryable Datasource Locations' at this link:-
http://www.cognifide.com/blogs/sitecore/reduce-multisite-chaos-with-sitecore-queries/
However I don't know where to start to get this working for other rendering parameter fields.
Any ideas? (I'm using Sitecore 6.6 Update 5)
Unfortunately, the pipeline mentioned in Adam Najmanowicz's answer works for some other types, like Droplink and Multilist, but the pipeline isn't run for Droptree fields.
After looking into this deeper I found that the Source of a Droptree field IS using the wrong context item, as Adam mentioned, but the code comes from the Droptree field itself:-
Sitecore.Shell.Applications.ContentEditor.Tree, Sitecore.Kernel
Utilising the query string code from Adam's answer, we can create a 'fixed' Droptree custom field, that is almost the same as the regular Droptree but will use the correct context item instead.
The code will inherit from the normal Tree control, and only change the way that the Source property is set.
public class QueryableTree : Sitecore.Shell.Applications.ContentEditor.Tree
{
// override the Source property from the base class
public new string Source
{
get
{
return StringUtil.GetString(new string[]
{
base.Source // slightly altered from the original
});
}
set
{
Assert.ArgumentNotNull(value, "value");
if (!value.StartsWith("query:", StringComparison.InvariantCulture))
{
base.Source = value; // slightly altered from the original
return;
}
Item item = Client.ContentDatabase.GetItem(this.ItemID);
// Added code that figures out if we're looking at rendering parameters,
// and if so, figures out what the context item actually is.
string url = WebUtil.GetQueryString();
if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
{
FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
var currentItemId = parameters["contentitem"];
if (!string.IsNullOrEmpty(currentItemId))
{
Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
item = Sitecore.Data.Database.GetItem(contentItemUri);
}
}
if (item == null)
{
return;
}
Item item2 = item.Axes.SelectSingleItem(value.Substring("query:".Length));
if (item2 == null)
{
return;
}
base.Source = item2.ID.ToString(); // slightly altered from the original
}
}
The above code is pretty much the same as the Source property on the base Tree field, except that we figure out the proper context item from the URL if we've detected that we're in the rendering parameters dialog.
To create the custom field, you just need to edit the Web.Config file as described here. Then add the custom field to the core database as described here.
This means that parameters can now have queries for their source, allowing us to limit the available items to the content editor. (Useful for multi-site solutions).
The key here would be to set the Field Editor's context to be relative to the item you are editing instead of the Rendering parameters (that I think it has by default).
So you could have processor:
public class ResolveRelativeQuerySource
{
public void Process(GetLookupSourceItemsArgs args)
{
Assert.IsNotNull(args, "args");
if (!args.Source.StartsWith("query:"))
return;
Item contextItem = null;
string url = WebUtil.GetQueryString();
if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
{
FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
var currentItemId = parameters["contentitem"];
if (!string.IsNullOrEmpty(currentItemId))
{
Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
contextItem = Sitecore.Data.Database.GetItem(contentItemUri);
}
}
else
{
contextItem = args.Item;
}
}
}
hooked as:
<sitecore>
<pipelines>
<getLookupSourceItems>
<processor patch:before="*[#type='Sitecore.Pipelines.GetLookupSourceItems.ProcessQuerySource, Sitecore.Kernel']"
type="Cognifide.SiteCore.Logic.Processors.ResolveRelativeQuerySource, Cognifide.SiteCore" />
</getLookupSourceItems>
</pipelines>
</sitecore>
Together with ResolveQueryableDatasources from Przemek's blog this should solve your problem.

How to search for Items that doesn't contain certain keywords using Sitecore Advanced Database Crawler?

I have millions of items in the database. And user can search those items based on the keyword. In the search function, I need to provide a feature that search for NOT that keyword.
For example Item A has a field called msg and the value is Sitecore is awesome and great.
In the search box, user can check the checkbox that indicates to show any item that doesn't contain the keyword. Maybe user key in is keyword, so Item A will not be displayed or retrieved by ADC.
Edit: Currently I'm using sitecore6.6, thus Search method has been deprecated. I have tried the Not keyword by using Occurrence.MustNot, but it doesn't return any result.
The Sitecore Advanced Database Crawler is an extension of the Sitecore.Search API and Sitecore.Search API is a wrapper around Lucene.
In Lucene you can user queries with NOT or - to exclude something like "Sitecore NOT awesome" or "Sitecore -awesome".
To exclude something you need at least one include term.
Not sure if it works, but give it a try.
This is untested, but you might have luck by using a MatchAllDocsQuery and supplying the keywords in the form of a Filter.
BooleanQuery booleanQuery = new BooleanQuery();
QueryParser queryParser = new QueryParser("msg", new StandardAnalyzer());
Query userQuery = queryParser.Parse("Sitecore is awesome and great");
booleanQuery.Add(userQuery, reverseQuery.Checked ? BooleanClause.Occur.MUST_NOT : BooleanClause.Occur.MUST);
MatchAllDocsQuery matchAllQuery = new MatchAllDocsQuery();
Filter filter = new QueryFilter(booleanQuery);
using (QueryRunner queryRunner = new QueryRunner("myIndex"))
{
var skinnyItems = queryRunner.RunQuery(matchAllQuery, filter, ...)
}
What I have done to NOT include the keywords associated with the result item set is this:
protected List<Item> getSearchResults(string queryToSearch, string selectedFilter, string notToSearch)
{
Database db = Sitecore.Context.Database;
var index = SearchManager.GetIndex("siteSearchIndexName");
using (SortableIndexSearchContext context = new SortableIndexSearchContext(index))
{
if (!String.IsNullOrWhiteSpace(query))
{
query.ToLower();
CombinedQuery cq = new CombinedQuery();
QueryBase qbKeyword = new FieldQuery("_orderkeywordpair", query);
QueryBase qbContent = new FieldQuery("_content", query);
QueryBase qbHtml = new FieldQuery("html", query);
if (!String.IsNullOrWhiteSpace(selectedFilter) && selectedFilter.ToLower() != "all")
{
QueryBase qbFilter = new FieldQuery("_pagetype", selectedFilter);
cq.Add(qbFilter, QueryOccurance.Must);
}
cq.Add(qbKeyword, QueryOccurance.Should);
cq.Add(qbContent, QueryOccurance.Must);
cq.Add(qbHtml, QueryOccurance.MustNot);
SearchHits hits = context.Search(cq);

Accessing Item Fields via Sitecore Web Service

I am creating items on the fly via Sitecore Web Service. So far I can create the items from this function:
AddFromTemplate
And I also tried this link: http://blog.hansmelis.be/2012/05/29/sitecore-web-service-pitfalls/
But I am finding it hard to access the fields. So far here is my code:
public void CreateItemInSitecore(string getDayGuid, Oracle.DataAccess.Client.OracleDataReader reader)
{
if (getDayGuid != null)
{
var sitecoreService = new EverBankCMS.VisualSitecoreService();
var addItem = sitecoreService.AddFromTemplate(getDayGuid, templateIdRTT, "Testing", database, myCred);
var getChildren = sitecoreService.GetChildren(getDayGuid, database, myCred);
for (int i = 0; i < getChildren.ChildNodes.Count; i++)
{
if (getChildren.ChildNodes[i].InnerText.ToString() == "Testing")
{
var getItem = sitecoreService.GetItemFields(getChildren.ChildNodes[i].Attributes[0].Value, "en", "1", true, database, myCred);
string p = getChildren.ChildNodes[i].Attributes[0].Value;
}
}
}
}
So as you can see I am creating an Item and I want to access the Fields for that item.
I thought that GetItemFields will give me some value, but finding it hard to get it. Any clue?
My advice would be to not use the VSS (Visual Sitecore Service), but write your own service specifically for the thing you want it to do.
This way is usually more efficient because you can do exactly the thing you want, directly inside the service, instead of making a lot of calls to the VSS and handle your logic on the clientside.
For me, this has always been a better solution than using the VSS.
I am assuming you are looking to find out what the fields looks like and what the field IDs are.
You can call GetXml with the ID, it returns the item and all the versions and fields set in it, it won't show fields you haven't set.