Umbraco, FullTextSearch - including search of Media section? - umbraco

I have installed FullTextSearch (http://fulltextsearch.codeplex.com/) on my website and it is working good. Whatever keyword I type, FullTextSearch goes through content and tries to find that keyword.
But when I type some keyword which is only available in Media section in back-end of Umbraco, I can't find anything even with the fact that I have hunderds of files in Media folder??
Can anyone explain what is the issue and why FullTextSearch can't find anything from Media Section?
Many thanks in advance! Adi

By default, Umbraco doesn't index media items. There's a plugin out there called Cog Umbraco Examine Media Indexer which adds an index for the media section of the site, and can index PDFs, Word and Excel files, text files, and other file types that the Apache Tika project can parse.
If you take the Media Indexer approach, then you'll have to customize your search page to utilize both the Full Text Search and Media Indexer indexes. our.umbraco.org provides one approach for combining multiple search indexes. Note that the Media Indexer creates an index named "MediaIndexSet" and the Full Text Search plugin creates an index named "FullTextIndexer".
EDIT
Here's an example of how you can create a MultiIndexSearcher to combine the two search indexes:
var indexes = new[] {"FullTextIndexer", "MediaIndexer"};
var directories = new List<DirectoryInfo>();
foreach (var index in indexes)
{
var indexer = ExamineManager.Instance.IndexProviderCollection[index];
var directory =
new DirectoryInfo(
((LuceneIndexer) indexer).LuceneIndexFolder.FullName.Replace("\\Index", ""));
directories.Add(directory);
}
var searcher = new MultiIndexSearcher(directories, new StandardAnalyzer());
Then you can use the searcher object to perform your searching:
var criteria = searcher.CreateSearchCriteria();
var query = criteria.RawQuery("name:\"search terms\""); // Replace with actual query
var results = searcher.Search(query);

Related

How to generate HATEOAS links for paging in ASP.NET Core 6 Web API controller to return in Link header?

I need to create HATEOAS pagination links for first, last, next, and previous and add them to the Link header of the response.
A PagedResultDTO is returned from the application layer to the controller in the API layer. Besids the records it holds information about TotalCount, PageNumber, PageSize, and PageCount.
I'm confused with the options provided by the framework. Many posts use UriHelper, but it seems that LinkGenerator is the newer and the current helper class. Looking at the documentation I don't understand the difference of the various methods that LinkGenerator provides. Is there any good post that explains the use cases for the various methods?
Since a request can have various other query parameters e.g. for sorting, filtering, searching, embedding they need to be kept and returned when the links get created. E.g. a controller declaration can look like:
public ActionResult<PagedResultDTO<GetCountriesDTO>> GetCountries(
[FromHeader(Name = "If-None-Match")] string? eTags,
[CommaSeparated] IEnumerable<string> languages,
string? sort,
string? search,
int? pageNumber,
int? pageSize)
([CommaSeperated] is an extension that accepts multiple values separated by comma and creates a List without having to repeat the query parameter. If pageNumber and pageCount are not set and don't have default values set in a specific controller method my use case handlers will set default values.)
For a request like:
GET https://example.com/api/countries?languages=de-DE,en-US&sort=name
I need to generate:
<https://example.com/api/countries?languages=de-DE,en-US&sort=name&pageNumber=2&pageSize=20>; rel="next",
<https://example.com/api/countries?languages=de-DE,en-US&sort=name&pageNumber=1&pageSize=20>; rel="first",
<https://example.com/api/countries?languages=de-DE,en-US&sort=name&pageNumber=10&pageSize=20>; rel="last"
All query parameters not related to paging need to be kept in the generated links. Does LinkGenerator support a straightforward way to generate those pagination links? Is there any structure that supports the Link header format already? I wonder if should just create the links manually using the Request object?
I created now my own code using a dictionary that holds the passed query parameters.
Dictionary<string, string> query = new();
foreach (KeyValuePair<string, StringValues> item in httpContext.Request.Query)
{
query.Add(item.Key, string.Join(",", item.Value));
}
I then update pageNumber and pageSize for each link. If it was not set before and defaults are used the parameters are added, e.g.:
query["pageNumber"] = "1";
query["pageSize"] = pageSize.ToString();
I then use LinkGenerator to create the link:
linkGenerator.GetUriByAction(httpContext, values: query)

How to convert IPublisedContent list to Strongly typed List - Umbraco Model Builder

I'm using the Models Builder to create C# classes for all my document types, in my custom controller I am retriving all the content nodes that are of a specfic type:
var viewModel = new HomePageViewModel(model);
var caseStudyNodes = viewModel.Content.AncestorOrSelf().Descendants(CaseStudy.ModelTypeAlias).ToList();
the caseStudyNodes variable is now a list of IPublished content. Is there a way to get this as a list of CaseStudy objects?
I'm using Umbraco 8 is there a diffrence between how you would do it if you where using umbraco 7
Found the answer to my own question, just in case it helps someone else.
var caseStudyList = caseStudyNodes.Select(c => new CaseStudy(c));

Umbraco - Get Property by type

I'm new to Umbraco, so this may not even be feasible. I've created my own Datatype using Archetype and want to be able to get an instance of that type on the page by type, not alias.
I know that I can do the following:
model.Content.GetPropertyValue("myAlias")
But I want to know if it's feasible to get the property by the type. Something along the lines of:
model.Content.GetPropertiesByType("TypeName")
which would return a list of controls on the page of that type?
Is this feasible?
It's possible, but not exactly straight forward.
Take a look at the available Umbraco Data Services - you'll need to retrieve the DataTypeDefinitions from the DataTypeService and retrieve the ContentType for the Model's IPublishedContent using the ContentTypeService.
Once you have these, you can match up the PropertyTypes on the ContentType with the retrieved DataTypeDefionitions based on the PropertyType's DataTypeDefinitionId.
The PropertyTypes have an Alias property which will match up with the Property Aliases on the Content itself.
You can use the content service if you get the id of the datatype you trying to find the multiples of from the url when you edit/create the datatype.
#foreach (var p in ApplicationContext.Current.Services.ContentService.GetById(Model.Content.Id).PropertyTypes.Where(p => p.DataTypeDefinitionId == -89))
{
<p>#p.DataTypeDefinitionId</p>
}

Is it possible to create an alternative for List in Orchard?

I just want to customize List view.
is it possible? I think yes. How?
How to name alternative for List of Content types, let say, "Question"
Sure. As the question is a little vague about context, I'll make assumptions. I'll assume that you have a controller action building the list (similar to what the blog is doing). From the action, you are usually building a list shape with code looking something like this:
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var things = _someServiceClass.GetThings(something)
.Skip(pager.GetStartIndex()).Take(pager.PageSize))
.Select(p => Shape.Thing_Summary(
Thing: p,
SomethingElseThatIsRelevantToTheTemplate: foo));
Shape list = Shape.List(Pager: pager);
list.AddRange(things);
list.Metadata.Alternates.Add("list_things");
return new ShapeResult(this, list);
See that line near the end? It's adding the alternate, so that you can build a specific template for that particular list by creating a file named list.things.cshtml in the views folder of your theme.

Get Umbraco node by path in c# (Language Switching)

I have a multi language site with the following structure:
siteroot
- en
-- home
-- login
-- etc.
- de
-- home
-- login
-- etc.
The content beneath the language nodes is not necessarily the same.
When switching languages I want to test if a parallel language node exists.
Currently I'm doing the following:
get current path
replace the language part of path
e.g. replace /en/login to /de/login
the closest I've found to test the existence of a page is:
XPathNodeIterator i = umbraco.library.GetXmlDocumentByUrl("http://localhost/de/login");
Debugging this shows, that umbraco actually hits the database. This can't be the best way to test the existence of a page.
Anybody have a better method at hand?
By the sounds your using the document class in cms.businesslogic.web namespace. This class is used for modifying/publishing nodes inside of umbraco.
Try using the node class that resides in umbraco.presentation.nodeFactory. This will interact with the in-memory XML cache only.
Node.GetCurrent() //static method - will give you the current loaded page.
Node.Parent //class property - will give parent method
The problem with the node class, that it can't take XPath queries (and will not give performance)
I've written a dynamic Linq provider that can be used to query the Umbraco XML structure using compiled xslt expressions. I going to be publishing in the next week or so. Let me know if your interested...
We have a multilingual site where we needed to do the same thing to set up our hreflang tags. There might be a better way, but I decided on building some Xpath to find out if a matching node exists in the other languages. We are using umbraco 7, and I would shy away from using NodeFactory if at all possible. It is depreciated. Using the umbraco helper won't hit the database, and is one of the best ways to query published content or media from umbraco for umbraco 7.
public static IPublishedContent GetLocalizedVersionOfPage(this IPublishedContent node, string regionName)
{
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
var ancestorNames = node.AncestorsOrSelf()
.Where(n => n.Level > 1)
.OrderBy(n => n.Level)
.Select(n => n.Name).ToList();
var xpath = new StringBuilder();
xpath.AppendFormat("/root/HomePage[#nodeName='{0}']", regionName);
foreach (var ancestorName in ancestorNames)
{
xpath.AppendFormat("/*[#nodeName='{0}']", ancestorName);
}
var matchingNode = umbracoHelper.TypedContentAtXPath(xpath.ToString()).FirstOrDefault();
return matchingNode;
}
The above method is an extension method on the IPublishedContent. It allows you to pass in the region and it checks to see if a node with the same path determined by node name exists in the specified region. I thought about using the urlname instead of the node name. You could do that as well and maybe even make this faster by skipping the piece of code that does the .AncestorsOrSelf(). It just depends on how you want it to work. In my case, I wanted it to find a match based on the node name even if the url path was different, so I had to do the .AncestorsOrSelf(). Hope this helps.
Another thing to consider is how you call this method. if you use a loop like this:
#foreach (var region in Umbraco.TypedContentAtRoot().Where(n => n.IsDocumentType("HomePage")))
{
var localizedVersion = currentPage.GetLocalizedVersionOfPage(region.Name);
if (localizedVersion != null)
{
<link rel="alternate" href="#localizedVersion.UrlAbsolute()" hreflang="#LocalizeUtils.GetCulture(region.Name)" />
}
}
You will end up getting the ancestors of the current node over and over once for each region because it calls .AncestorsOrSelf() every time you call GetLocalizedVersionOfPage(). It probably makes sense to refactor the GetLocalizedVersionOfPage method so you only have to call .AncestorsOrSelf once. If you do this sort of thing too many times, it starts to affect performance (especially if your site is very nested).

Resources