Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Ross Ekberg 124 posts 364 karma points
    Feb 05, 2016 @ 18:33
    Ross Ekberg
    0

    EZSearch does in fact work with the WebForms rendering engine

    I was running Umbraco v6.1.6, using XSLT Search. We have many custom macros on our site, all of which use WebForms. When we upgraded to v7, I had to enable the WebForms rendering engine rather than the MVC rendering engine. XSLT Search doesn't work in v7, so I had to replace it with something. EZSearch appeared to be the best option. However, one of the requirements is that the MVC rendering engine be enabled. I found this not to be true. Obviously, I had to work through several issues, but in the end I was not only able to get the search working, I was able to enhance a few things.

  • Jan Skovgaard 11280 posts 23678 karma points MVP 10x admin c-trib
    Feb 05, 2016 @ 19:11
    Jan Skovgaard
    0

    Hi Ross

    Care to share the steps you took to get things working? :) Other might be interested and can benefit from your efforts if they for some reason cannot switch to MVC instead of webforms.

    /Jan

  • Ross Ekberg 124 posts 364 karma points
    Feb 05, 2016 @ 20:39
    Ross Ekberg
    1

    There was a lot of discovery involved. And being that my installation of Umbraco is not "out of the box", some of the things I had to do wouldn't necessarily apply to others. That said, I can try to elaborate.

    It should be noted that I reviewed the log files quite a bit in order to determine where the problems were.

    I installed the package through Umbraco's built in installer. The first real problem I had was that it seemed it wasn't searching the body text. The documentation said there were defaults and, upon reviewing the partial view, I saw that there were. However, it seems that when we installed Umbraco originally, the "bodyText" property was called something else. I had to go into the document type to figure out what it was and put it into the macro's properties, overriding the defaults.

    Doing that brought up some new errors. I kept getting an "object reference" not being set error. I went into the partial view and found the 'RenderContentResult' method, and put the code in a try/catch block. Doing this allow me to see that any page that has a Contour form on it wasn't working. That is a separate issue I have yet to deal with.

    Now I was able to see results. However, the results were very ugly. Here is the original line of code that outputs the results:

    @Highlight(Truncate(Umbraco.StripHtml(result.GetPropertyValue(field).ToString()), model.PreviewLength), model.SearchTerms)
    

    This line does three things. First, it strips out the HTML. Next, it truncates the content to the length you set in the properties (default is 250 characters). Finally, it highlights the keywords you searched for. All three of these things needed work.

    First, the 'Umbraco.StripHtml' method wasn't working. I tried several different things but ultimately replaced it with a Regex. I will show the code for that at the end when I post everything.

    Second, the 'Truncate' method always starts from the beginning of the content and cuts off 250 characters (or the value you entered) from there. The result didn't always include the keyword(s) you searched for. I felt it would make more sense to ensure that the keyword was a part of the truncated result. To achieve this, I found the 'Truncate' method and added the following code at the top of the method:

    var indexOfTerm = input.IndexOf("<found>");
    var startingPoint = indexOfTerm - 125;
    if (startingPoint < 0) {startingPoint = 0;}
    input = input.Substring(startingPoint);
    

    Two things to note here. First, you see the number 125? That number is in the middle of 250, which will put the keyword in the middle of the result. Second, you see the reference to the "found" element? That is an element that gets inserted around the keywords in the body text. I will explain that in a bit. So basically, the code above finds the index of the "found" element, adjusts that index back 125, checks to ensure that the index isn't below zero, and finally creates a substring of the input based on that index.

    Third, the 'Highlight' method only made the keyword(s) bold. I thought it would be nicer if it was bold and actually highlighted. This could have been achieved through inline styling, but I thought it would be better to put a class on it and do it through a stylesheet. I went into the 'Highlight' method and replaced the "strong" element with a "span" that had a class on it called 'highlight'. I then created a stylesheet that had the following code in it:

    .ezsearch-result {
    background-color:white;
    }
    
    .ezsearch-result .highlight {
    background-color:#FFFF00;
    font-weight: bold;
    }
    

    This makes the keywords both bold and highlighted. It also makes the background color of each search result white, which worked on my website but may not be necessary on yours.

    Finally, there was an issue with the order of operations. To properly truncate the result, it needs to be known where the keywords are. The 'Highlight' method is already locating the keywords. My first thought was to strip the HTML first, highlight the keywords second, then truncate the result. The problem was that the 'Truncate' method returned a string type, which the macro renders as plain text. The 'Highlight' method returns an iHtmlString type, which the macro renders as HTML. So the problem was that if I hightlight prior to truncating, the macro won't render the HTML used to highlight the keywords. I could have rewritten the 'Truncate' method to return an iHtmlString type, but I didn't want to. Instead, I created a new method called 'Locate'. This method is a copy of the 'Highlight' method and does the same thing, but instead of inserting code to highlight the text, it inserts the "found" element that I mentioned previously.

    So now the order of operations is: Strip HTML, locate keywords, truncate, highlight.

    Here is the line of code that does all that, which replaces the original one mentioned above:

    @Highlight(Truncate(Locate(Regex.Replace(result.GetPropertyValue(field).ToString(), @"<[^>]*>", String.Empty), model.SearchTerms),model.PreviewLength), model.SearchTerms)
    

    And here are the complete modified methods:

      // Highlights all occurances of the search terms in a body of text
    public IHtmlString Highlight(string input, IEnumerable<string> searchTerms)
    {
        input = HttpUtility.HtmlDecode(input);
    
        foreach (var searchTerm in searchTerms)
        {
            input = Regex.Replace(input, Regex.Escape("<found>"+searchTerm+"</found>"), @"<span class='highlight'>$0</span>", RegexOptions.IgnoreCase);
        }
    
        return new HtmlString(input);
    }
    
    // Locates all occurances of the search terms in a body of text and prefixes them with a special character
    public IHtmlString Locate(String input, IEnumerable<string> searchTerms)
    {
        input = HttpUtility.HtmlDecode(input);
    
        foreach (var searchTerm in searchTerms)
        {
            input = Regex.Replace(input, Regex.Escape(searchTerm), @"<found>$0</found>", RegexOptions.IgnoreCase);
        }
    
        return new HtmlString(input);                            
    }
    
    // Truncates a string on word breaks
    public string Truncate(string input, int maxLength)
    {
        var indexOfTerm = input.IndexOf("<found>");
        var startingPoint = indexOfTerm - 125;
        if (startingPoint < 0) {startingPoint = 0;}
        input = input.Substring(startingPoint);
    
        var truncated = Umbraco.Truncate(input, maxLength, true).ToString();
        if (truncated.EndsWith("&hellip;"))
        {
            var lastSpaceIndex = truncated.LastIndexOf(' ');
            if(lastSpaceIndex > 0)
            {
                truncated = truncated.Substring(0, lastSpaceIndex) + "&hellip;";
            }
        }
    
        return truncated;
    }
    

    So yea, that's how I did it. I realize now, after typing all this up, it wouldn't have been too difficult to adjust the 'Truncate' method to return an iHtmlString type, which would eliminate the need for the additional 'Locate' method. However, it works so I'm not going to make any changes to it.

Please Sign in or register to post replies

Write your reply to:

Draft