Advertisement
Guest User

Untitled

a guest
Nov 15th, 2017
203
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.57 KB | None | 0 0
  1. @using System.Globalization
  2. @using System.Text
  3. @using System.Text.RegularExpressions
  4. @using Examine
  5. @using Umbraco.Core.Logging
  6. @using Umbraco.Web.Models
  7. @inherits Umbraco.Web.Macros.PartialViewMacroPage
  8. @{
  9. int parsedInt;
  10.  
  11. // Parse querystring / macro parameter
  12. var model = new SearchViewModel
  13. {
  14. SearchTerm = CleanseSearchTerm(("" + Request["q"]).ToLower(CultureInfo.InvariantCulture)),
  15. CurrentPage = int.TryParse(Request["p"], out parsedInt) ? parsedInt : 1,
  16.  
  17. PageSize = GetMacroParam(Model, "pageSize", s => int.Parse(s), 10),
  18. RootContentNodeId = GetMacroParam(Model, "rootContentNodeId", s => int.Parse(s), -1),
  19. RootMediaNodeId = GetMacroParam(Model, "rootMediaNodeId", s => int.Parse(s), -1),
  20. IndexType = GetMacroParam(Model, "indexType", s => s.ToLower(CultureInfo.InvariantCulture), ""),
  21. SearchFields = GetMacroParam(Model, "searchFields", s => SplitToList(s), new List<string> { "nodeName", "metaTitle", "metaDescription", "metaKeywords", "bodyText" }),
  22. PreviewFields = GetMacroParam(Model, "previewFields", s => SplitToList(s), new List<string> { "bodyText" }),
  23. PreviewLength = GetMacroParam(Model, "previewLength", s => int.Parse(s), 250),
  24. HideFromSearchField = GetMacroParam(Model, "hideFromSearchField", "umbracoNaviHide"),
  25. SearchFormLocation = GetMacroParam(Model, "searchFormLocation", s => s.ToLower(), "bottom")
  26. };
  27.  
  28. // Validate values
  29. if (model.IndexType != UmbracoExamine.IndexTypes.Content &&
  30. model.IndexType != UmbracoExamine.IndexTypes.Media)
  31. {
  32. model.IndexType = "";
  33. }
  34.  
  35. if (model.SearchFormLocation != "top"
  36. && model.SearchFormLocation != "bottom"
  37. && model.SearchFormLocation != "both"
  38. && model.SearchFormLocation != "none")
  39. {
  40. model.SearchFormLocation = "bottom";
  41. }
  42.  
  43. // ====================================================
  44. // Comment the next if statement out if you want a root
  45. // node id of -1 to search content across all sites
  46. // and not just the current site.
  47. // ====================================================
  48. //if (model.RootContentNodeId <= 0)
  49. //{
  50. // model.RootContentNodeId = Model.Content.AncestorOrSelf(1).Id;
  51. //}
  52.  
  53. // If searching on umbracoFile, also search on umbracoFileName
  54. if (model.SearchFields.Contains("umbracoFile") && !model.SearchFields.Contains("umbracoFileName"))
  55. {
  56. model.SearchFields.Add("umbracoFileName");
  57. }
  58.  
  59. // Check the search term isn't empty
  60. if(!string.IsNullOrWhiteSpace(model.SearchTerm))
  61. {
  62. // Tokenize the search term
  63. model.SearchTerms = Tokenize(model.SearchTerm);
  64.  
  65. // Perform the search
  66. var searcher = ExamineManager.Instance.SearchProviderCollection["ExternalSearcher"];
  67. var criteria = searcher.CreateSearchCriteria();
  68. var query = new StringBuilder();
  69. query.AppendFormat("-{0}:1 ", model.HideFromSearchField);
  70.  
  71. // Set search path
  72. var contentPathFilter = model.RootContentNodeId > 0
  73. ? string.Format("__IndexType:{0} +searchPath:{1} -template:0", UmbracoExamine.IndexTypes.Content, model.RootContentNodeId)
  74. : string.Format("__IndexType:{0} -template:0", UmbracoExamine.IndexTypes.Content);
  75.  
  76. var mediaPathFilter = model.RootMediaNodeId > 0
  77. ? string.Format("__IndexType:{0} +searchPath:{1}", UmbracoExamine.IndexTypes.Media, model.RootMediaNodeId)
  78. : string.Format("__IndexType:{0}", UmbracoExamine.IndexTypes.Media);
  79.  
  80. switch (model.IndexType)
  81. {
  82. case UmbracoExamine.IndexTypes.Content:
  83. query.AppendFormat("+({0}) ", contentPathFilter);
  84. break;
  85. case UmbracoExamine.IndexTypes.Media:
  86. query.AppendFormat("+({0}) ", mediaPathFilter);
  87. break;
  88. default:
  89. query.AppendFormat("+(({0}) ({1})) ", contentPathFilter, mediaPathFilter);
  90. break;
  91. }
  92.  
  93. // Ensure page contains all search terms in some way
  94. foreach (var term in model.SearchTerms)
  95. {
  96. var groupedOr = new StringBuilder();
  97. foreach (var searchField in model.SearchFields)
  98. {
  99. groupedOr.AppendFormat("{0}:{1}* ", searchField, term);
  100. }
  101. query.Append("+(" + groupedOr.ToString() + ") ");
  102. }
  103.  
  104. // Rank content based on positon of search terms in fields
  105. for (var i = 0; i < model.SearchFields.Count; i++)
  106. {
  107. foreach (var term in model.SearchTerms)
  108. {
  109. query.AppendFormat("{0}:{1}*^{2} ", model.SearchFields[i], term, model.SearchFields.Count - i);
  110. }
  111. }
  112.  
  113. var criteria2 = criteria.RawQuery(query.ToString());
  114.  
  115. var results = searcher.Search(criteria2)
  116. .Where(x => (
  117. !Umbraco.IsProtected(int.Parse(x.Fields["id"]), x.Fields["path"]) ||
  118. (
  119. Umbraco.IsProtected(int.Parse(x.Fields["id"]), x.Fields["path"]) &&
  120. Umbraco.MemberHasAccess(int.Parse(x.Fields["id"]), x.Fields["path"])
  121. )) && (
  122. (x.Fields["__IndexType"] == UmbracoExamine.IndexTypes.Content && Umbraco.TypedContent(int.Parse(x.Fields["id"])) != null) ||
  123. (x.Fields["__IndexType"] == UmbracoExamine.IndexTypes.Media && Umbraco.TypedMedia(int.Parse(x.Fields["id"])) != null)
  124. ))
  125. .ToList();
  126.  
  127. model.AllResults = results;
  128.  
  129. model.TotalResults = results.Count;
  130. model.TotalPages = (int)Math.Ceiling((decimal)model.TotalResults / model.PageSize);
  131. model.CurrentPage = Math.Max(1, Math.Min(model.TotalPages, model.CurrentPage));
  132.  
  133. // Page the results
  134. model.PagedResults = model.AllResults.Skip(model.PageSize * (model.CurrentPage - 1)).Take(model.PageSize);
  135.  
  136. LogHelper.Debug<string>("[ezSearch] Searching Lucene with the following query: " + query.ToString());
  137.  
  138. if (!model.PagedResults.Any())
  139. {
  140. // No results found, so render no results view
  141. if(model.SearchFormLocation != "none")
  142. {
  143. @RenderForm(model)
  144. }
  145. @RenderNoResults(model)
  146. }
  147. else
  148. {
  149. @RenderResultsRange(model)
  150.  
  151. // Render out the results
  152. if (model.SearchFormLocation == "top" || model.SearchFormLocation == "both")
  153. {
  154. @RenderForm(model)
  155. }
  156.  
  157.  
  158. @RenderSummary(model)
  159. @RenderResults(model)
  160.  
  161. if(model.TotalPages > 1)
  162. {
  163. @RenderPager(model)
  164. }
  165. if (model.SearchFormLocation == "bottom" || model.SearchFormLocation == "both")
  166. {
  167. @RenderForm(model)
  168. }
  169. }
  170. }
  171. else
  172. {
  173. // Empty search term so just render the form
  174. if(model.SearchFormLocation != "none")
  175. {
  176. @RenderForm(model)
  177. }
  178. }
  179. }
  180.  
  181. @*
  182. ==================================================
  183. Render Functions
  184. ==================================================
  185. *@
  186.  
  187. @helper RenderForm(SearchViewModel model)
  188. {
  189. <form action="" method="GET" class="ezsearch-form">
  190. <input type="text" name="q" placeholder="@(@Umbraco.GetDictionaryValue("search_placeholder"))" value="@(model.SearchTerm)" class="ezsearch-input"/>
  191. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0" x="0px" y="0px" width="50" height="50" viewBox="0 0 50 50" style="null" class="icon icons8-Search ezsearch"><path style="text-indent:0;text-align:start;line-height:normal;text-transform:none;block-progression:tb;-inkscape-font-specification:Bitstream Vera Sans" d="M 21 3 C 11.623004 3 4 10.623004 4 20 C 4 29.376996 11.623004 37 21 37 C 24.709505 37 28.140329 35.803849 30.9375 33.78125 L 44.09375 46.90625 L 46.90625 44.09375 L 33.90625 31.0625 C 36.460556 28.087561 38 24.221313 38 20 C 38 10.623004 30.376996 3 21 3 z M 21 5 C 29.296116 5 36 11.703884 36 20 C 36 28.296116 29.296116 35 21 35 C 12.703884 35 6 28.296116 6 20 C 6 11.703884 12.703884 5 21 5 z" color="#000" overflow="visible" enable-background="accumulate" font-family="Bitstream Vera Sans"></path></svg>
  192. </form>
  193. }
  194.  
  195. @helper RenderSummary(SearchViewModel model)
  196. {
  197. <div class="ezsearch-summary">
  198. <p>@FormatHtml(GetDictionaryValue("[ezSearch] Summary", "Your search for <strong>\"{0}\"</strong> matched <strong>{1}</strong> page(s)."), model.SearchTerm, model.TotalResults)</p>
  199. </div>
  200. }
  201.  
  202. @helper RenderResultsRange(SearchViewModel model)
  203. {
  204. var startRecord = ((model.CurrentPage - 1)*model.PageSize) + 1;
  205. var endRecord = Math.Min(model.TotalResults, (startRecord - 1) + model.PageSize);
  206.  
  207. <div class="ezsearch-result-count">
  208. @* Default - not shown *@
  209. @*<p>@FormatHtml(GetDictionaryValue("[ezSearch] Results Range", "Showing results <strong>{0}</strong> to <strong>{1}</strong>."), startRecord, endRecord)</p>*@
  210. </div>
  211. }
  212.  
  213. @helper RenderResults(SearchViewModel model)
  214. {
  215. <div class="ezsearch-results">
  216. @foreach (var result in model.PagedResults)
  217. {
  218. switch (result.Fields["__IndexType"])
  219. {
  220. case UmbracoExamine.IndexTypes.Content:
  221. var contentItem = Umbraco.TypedContent(result.Fields["id"]);
  222. @RenderContentResult(model, contentItem)
  223. break;
  224. case UmbracoExamine.IndexTypes.Media:
  225. var mediaItem = Umbraco.TypedMedia(result.Fields["id"]);
  226. @RenderMediaResult(model, mediaItem)
  227. break;
  228. }
  229. }
  230. </div>
  231. }
  232.  
  233. @helper RenderContentResult(SearchViewModel model, IPublishedContent result)
  234. {
  235. <div class="ezsearch-result">
  236. <h2><a href="@result.Url">@result.Name</a></h2>
  237. <a href="@result.Url" class="searchUrl">@result.Url</a>
  238. @foreach (var field in model.PreviewFields.Where(field => result.HasValue(field)))
  239. {
  240. <p>@Highlight(Truncate(Umbraco.StripHtml(result.GetPropertyValue(field).ToString()), model.PreviewLength), model.SearchTerms)</p>
  241. break;
  242. }
  243. </div>
  244. }
  245.  
  246. @helper RenderMediaResult(SearchViewModel model, IPublishedContent result)
  247. {
  248. <div class="ezsearch-result">
  249. <h2><a href="@(result.GetPropertyValue<string>("umbracoFile"))" class="@(result.GetPropertyValue<string>("umbracoExtension"))">@result.Name</a></h2>
  250. @foreach (var field in model.PreviewFields.Where(field => result.HasValue(field)))
  251. {
  252. <p>@Highlight(Truncate(Umbraco.StripHtml(result.GetPropertyValue(field).ToString()), model.PreviewLength), model.SearchTerms)</p>
  253. break;
  254. }
  255. </div>
  256. }
  257.  
  258. @helper RenderPager(SearchViewModel model)
  259. {
  260. <div class="ezsearch-pager">
  261. <p>
  262. @if (model.CurrentPage > 1) {
  263. <a class="prev" href="?q=@(model.SearchTerm)&p=@(model.CurrentPage-1)">@(GetDictionaryValue("[ezSearch] Previous", "Previous"))</a>
  264. } else {
  265. <span class="prev">@(GetDictionaryValue("[ezSearch] Previous", "Previous"))</span>
  266. }
  267.  
  268. @for (var i = 1; i <= model.TotalPages; i++)
  269. {
  270. if(i == model.CurrentPage) {
  271. <span class="page">@i</span>
  272. } else {
  273. <a class="page" href="?q=@(model.SearchTerm)&p=@(i)">@i</a>
  274. }
  275. }
  276.  
  277. @if (model.CurrentPage < model.TotalPages) {
  278. <a class="next" href="?q=@(model.SearchTerm)&p=@(model.CurrentPage + 1)">@(GetDictionaryValue("[ezSearch] Next", "Next"))</a>
  279. } else {
  280. <span class="next">@(GetDictionaryValue("[ezSearch] Next", "Next"))</span>
  281. }
  282. </p>
  283. </div>
  284. }
  285.  
  286. @helper RenderNoResults(SearchViewModel model)
  287. {
  288. <div class="ezsearch-no-results">
  289. <p>@FormatHtml(GetDictionaryValue("[ezSearch] No Results", "No results found for search term <strong>{0}</strong>."), model.SearchTerm)</p>
  290. </div>
  291. }
  292.  
  293. @functions
  294. {
  295. // ==================================================
  296. // Helper Functions
  297. //==================================================
  298.  
  299. // Cleanse the search term
  300. public string CleanseSearchTerm(string input)
  301. {
  302. return Umbraco.StripHtml(input).ToString();
  303. }
  304.  
  305. // Splits a string on space, except where enclosed in quotes
  306. public IEnumerable<string> Tokenize(string input)
  307. {
  308. return Regex.Matches(input, @"[\""].+?[\""]|[^ ]+")
  309. .Cast<Match>()
  310. .Select(m => m.Value.Trim('\"'))
  311. .ToList();
  312. }
  313.  
  314. // Highlights all occurances of the search terms in a body of text
  315. public IHtmlString Highlight(IHtmlString input, IEnumerable<string> searchTerms)
  316. {
  317. return Highlight(input.ToString(), searchTerms);
  318. }
  319.  
  320. // Highlights all occurances of the search terms in a body of text
  321. public IHtmlString Highlight(string input, IEnumerable<string> searchTerms)
  322. {
  323. input = HttpUtility.HtmlDecode(input);
  324.  
  325. foreach (var searchTerm in searchTerms)
  326. {
  327. input = Regex.Replace(input, Regex.Escape(searchTerm), @"<strong>$0</strong>", RegexOptions.IgnoreCase);
  328. }
  329.  
  330. return new HtmlString(input);
  331. }
  332.  
  333. // Formats a string and returns as HTML
  334. public IHtmlString FormatHtml(string input, params object[] args)
  335. {
  336. return Html.Raw(string.Format(input, args));
  337. }
  338.  
  339. // Gets a dictionary value with a fallback
  340. public string GetDictionaryValue(string key, string fallback)
  341. {
  342. var value = Umbraco.GetDictionaryValue(key);
  343.  
  344. return !string.IsNullOrEmpty(value)
  345. ? value
  346. : fallback;
  347. }
  348.  
  349. // Truncates a string on word breaks
  350. public string Truncate(IHtmlString input, int maxLength)
  351. {
  352. return Truncate(input.ToString(), maxLength);
  353. }
  354.  
  355. // Truncates a string on word breaks
  356. public string Truncate(string input, int maxLength)
  357. {
  358. var truncated = Umbraco.Truncate(input, maxLength, true).ToString();
  359. if (truncated.EndsWith("&hellip;"))
  360. {
  361. var lastSpaceIndex = truncated.LastIndexOf(' ');
  362. if(lastSpaceIndex > 0)
  363. {
  364. truncated = truncated.Substring(0, lastSpaceIndex) + "&hellip;";
  365. }
  366. }
  367.  
  368. return truncated;
  369. }
  370.  
  371. // Gets a macro parameter in a safe manner with fallback
  372. public string GetMacroParam(PartialViewMacroModel model, string key, string fallback)
  373. {
  374. return GetMacroParam(model, key, s => s, fallback);
  375. }
  376.  
  377. // Gets a macro parameter in a safe manner with fallback
  378. public TType GetMacroParam<TType>(PartialViewMacroModel model, string key, Func<string, TType> convert, TType fallback)
  379. {
  380. if(!model.MacroParameters.ContainsKey(key))
  381. {
  382. return fallback;
  383. }
  384.  
  385. var value = model.MacroParameters[key];
  386. if(value == null || value.ToString().Trim() == "")
  387. {
  388. return fallback;
  389. }
  390.  
  391. try
  392. {
  393. return convert(value.ToString());
  394. }
  395. catch (Exception)
  396. {
  397. return fallback;
  398. }
  399. }
  400.  
  401. // Splits a coma seperated string into a list
  402. public IList<string> SplitToList(string input)
  403. {
  404. return input.Split(',')
  405. .Select(f => f.Trim())
  406. .Where(f => !string.IsNullOrEmpty(f))
  407. .ToList();
  408. }
  409.  
  410. // ==================================================
  411. // Helper Classes
  412. //==================================================
  413.  
  414. public class SearchViewModel
  415. {
  416. // Query Parameters
  417. public string SearchTerm { get; set; }
  418. public IEnumerable<string> SearchTerms { get; set; }
  419. public int CurrentPage { get; set; }
  420.  
  421. // Options
  422. public int RootContentNodeId { get; set; }
  423. public int RootMediaNodeId { get; set; }
  424. public string IndexType { get; set; }
  425. public IList<string> SearchFields { get; set; }
  426. public IList<string> PreviewFields { get; set; }
  427. public int PreviewLength { get; set; }
  428. public int PageSize { get; set; }
  429. public string HideFromSearchField { get; set; }
  430. public string SearchFormLocation { get; set; }
  431.  
  432. // Results
  433. public int TotalResults { get; set; }
  434. public int TotalPages { get; set; }
  435.  
  436. public IEnumerable<SearchResult> AllResults { get; set; }
  437. public IEnumerable<SearchResult> PagedResults { get; set; }
  438. }
  439. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement