/// <summary> /// A method that will return the posts sorted by published date in an efficient way /// </summary> /// <param name="helper"></param> /// <param name="articulateArchiveId"></param> /// <param name="pager"></param> /// <returns></returns> public static IEnumerable<IPublishedContent> GetPostsSortedByPublishedDate(this UmbracoHelper helper, int articulateArchiveId, PagerModel pager) { var xPathNavigator = helper.UmbracoContext.ContentCache.GetXPathNavigator(false); var xPathChildren = $"//* [@id={articulateArchiveId}]/*[@isDoc]"; //Filter/Sort the children we're looking for with XML var xmlListItems = xPathNavigator.Select(xPathChildren) .Cast<XPathNavigator>() .OrderByDescending(x => { var publishedDate = DateTime.MinValue; var xmlNode = x.SelectSingleNode("publishedDate [not(@isDoc)]"); if (xmlNode == null) return publishedDate; publishedDate = xmlNode.ValueAsDateTime; return publishedDate; }) .Skip(pager.CurrentPageIndex * pager.PageSize) .Take(pager.PageSize); //Now we can select the IPublishedContent instances by Id var listItems = helper.TypedContent(xmlListItems.Select(x => int.Parse(x.GetAttribute("id", "")))); return listItems; }
public ListModel(IPublishedContent content, IEnumerable<IPublishedContent> listItems, PagerModel pager) : base(content) { if (listItems == null) throw new ArgumentNullException("listItems"); if (pager == null) throw new ArgumentNullException("pager"); _pager = pager; _listItems = listItems; }
private ActionResult RenderView(IRenderModel model, int? p = null) { var listNode = model.Content.Children .FirstOrDefault(x => x.DocumentTypeAlias.InvariantEquals("ArticulateArchive")); if (listNode == null) { throw new InvalidOperationException("An ArticulateArchive document must exist under the root Articulate document"); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } var rootPageModel = new ListModel(model.Content); //TODO: I wonder about the performance of this - when we end up with thousands of blog posts, // this will probably not be so efficient. I wonder if using an XPath lookup for batches of children // would work? The children count could be cached. I'd rather not put blog posts under 'month' nodes // just for the sake of performance. Hrm.... Examine possibly too. var totalPosts = listNode.Children.Count(); var pageSize = rootPageModel.PageSize; var totalPages = Convert.ToInt32(Math.Ceiling((double)totalPosts/pageSize)); //Invalid page, redirect without pages if (totalPages > 0 && totalPages < p) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "p=" + (p - 1) : p > 1 ? model.Content.Url : null); var listModel = new ListModel(listNode, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }
private ActionResult RenderView(IRenderModel model, int? p = null) { var listNode = model.Content.Children .FirstOrDefault(x => x.DocumentTypeAlias.InvariantEquals("ArticulateArchive")); if (listNode == null) { throw new InvalidOperationException("An ArticulateArchive document must exist under the root Articulate document"); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } var rootPageModel = new MasterModel(model.Content); //get the count with XPath, this will be the fastest var totalPosts = Umbraco.GetPostCount(listNode.Id); var pageSize = rootPageModel.PageSize; var totalPages = Convert.ToInt32(Math.Ceiling((double)totalPosts/pageSize)); //Invalid page, redirect without pages if (totalPages > 0 && totalPages < p) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "p=" + (p - 1) : p > 1 ? model.Content.Url : null); var listItems = Umbraco.GetPostsSortedByPublishedDate(listNode.Id, pager); var listModel = new ListModel(listNode, listItems, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }
public ActionResult Index(RenderModel model, int? maxItems) { if (!maxItems.HasValue) maxItems = 25; var listNode = model.Content.Children .FirstOrDefault(x => x.DocumentTypeAlias.InvariantEquals("ArticulateArchive")); if (listNode == null) { throw new InvalidOperationException("An ArticulateArchive document must exist under the root Articulate document"); } var pager = new PagerModel(maxItems.Value, 0, 1); var listItems = Umbraco.GetPostsSortedByPublishedDate(listNode.Id, pager); var rootPageModel = new ListModel(listNode, listItems, pager); var feed = FeedGenerator.GetFeed(rootPageModel, rootPageModel.Children<PostModel>()); return new RssResult(feed, rootPageModel); }
/// <summary> /// Used to render the search result listing (virtual node) /// </summary> /// <param name="model"></param> /// <param name="term"> /// The search term /// </param> /// <param name="provider"> /// The search provider name (optional) /// </param> /// <param name="p"></param> /// <returns></returns> public ActionResult Search(RenderModel model, string term, string provider = null, int? p = null) { var tagPage = model.Content as ArticulateVirtualPage; if (tagPage == null) { throw new InvalidOperationException("The RenderModel.Content instance must be of type " + typeof(ArticulateVirtualPage)); } //create a blog model of the main page var rootPageModel = new ListModel(model.Content.Parent); if (term == null) { //nothing to search, just render the view var emptyList = new ListModel(tagPage, Enumerable.Empty<IPublishedContent>(), new PagerModel(rootPageModel.PageSize, 0, 0)); return View(PathHelper.GetThemeViewPath(emptyList, "List"), emptyList); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } var splitSearch = term.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); //The fields to search on and their 'weight' (importance) var fields = new Dictionary<string, int> { {"markdown", 2}, {"richText", 2}, {"nodeName", 3}, {"tags", 1}, {"categories", 1}, {"umbracoUrlName", 3} }; //The multipliers for match types const int exactMatch = 5; const int termMatch = 2; var fieldQuery = new StringBuilder(); //build field query foreach (var field in fields) { //full exact match (which has a higher boost) fieldQuery.Append(string.Format("{0}:{1}^{2}", field.Key, "\"" + term + "\"", field.Value * exactMatch)); fieldQuery.Append(" "); //NOTE: Phrase match wildcard isn't really supported unless you use the Lucene // API like ComplexPhraseWildcardSomethingOrOther... //split match foreach (var s in splitSearch) { //match on each term, no wildcard, higher boost fieldQuery.Append(string.Format("{0}:{1}^{2}", field.Key, s, field.Value * termMatch)); fieldQuery.Append(" "); //match on each term, with wildcard fieldQuery.Append(string.Format("{0}:{1}*", field.Key, s)); fieldQuery.Append(" "); } } var criteria = provider == null ? ExamineManager.Instance.CreateSearchCriteria() : ExamineManager.Instance.SearchProviderCollection[provider].CreateSearchCriteria(); criteria.RawQuery(string.Format("+parentID:{0} +({1})", rootPageModel.BlogArchiveNode.Id, fieldQuery)); var searchProvider = provider == null ? ExamineManager.Instance.DefaultSearchProvider : ExamineManager.Instance.SearchProviderCollection[provider]; var searchResult = Umbraco.TypedSearch(criteria, searchProvider).ToArray(); //TODO: I wonder about the performance of this - when we end up with thousands of blog posts, // this will probably not be so efficient. I wonder if using an XPath lookup for batches of children // would work? The children count could be cached. I'd rather not put blog posts under 'month' nodes // just for the sake of performance. Hrm.... Examine possibly too. var totalPosts = searchResult.Count(); var pageSize = rootPageModel.PageSize; var totalPages = totalPosts == 0 ? 1 : Convert.ToInt32(Math.Ceiling((double)totalPosts / pageSize)); //Invalid page, redirect without pages if (totalPages < p) { return new RedirectToUmbracoPageResult(model.Content.Parent, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p - 1) : p > 1 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term : null); var listModel = new ListModel(tagPage, searchResult, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }
/// <summary> /// Constructor accepting an explicit list of child items /// </summary> /// <param name="content"></param> /// <param name="listItems"></param> /// <param name="pager"></param> /// <remarks> /// Default sorting by published date will be disabled for this list model, it is assumed that the list items will /// already be sorted. /// </remarks> public ListModel(IPublishedContent content, IEnumerable <IPublishedContent> listItems, PagerModel pager) : base(content) { if (content == null) { throw new ArgumentNullException(nameof(content)); } if (listItems == null) { throw new ArgumentNullException(nameof(listItems)); } if (pager == null) { throw new ArgumentNullException(nameof(pager)); } _pager = pager; _listItems = listItems; if (content.ContentType.Alias.Equals("ArticulateArchive")) { PageTitle = BlogTitle + " - " + BlogDescription; } else { PageTags = Name; } }
/// <summary> /// Constructor accepting an explicit list of child items /// </summary> /// <param name="content"></param> /// <param name="listItems"></param> /// <param name="pager"></param> /// <remarks> /// Default sorting by published date will be disabled for this list model, it is assumed that the list items will /// already be sorted. /// </remarks> public ListModel(IPublishedContent content, IEnumerable <IPublishedContent> listItems, PagerModel pager) : base(content) { if (listItems == null) { throw new ArgumentNullException("listItems"); } if (pager == null) { throw new ArgumentNullException("pager"); } _pager = pager; _disableSort = true; _listItems = listItems; }
/// <summary> /// Returns a list of the most recent posts /// </summary> /// <param name="helper"></param> /// <param name="masterModel"></param> /// <param name="count"></param> /// <returns></returns> public static IEnumerable<PostModel> GetRecentPosts(this UmbracoHelper helper, IMasterModel masterModel, int count) { var listNode = masterModel.RootBlogNode.Children .FirstOrDefault(x => x.DocumentTypeAlias.InvariantEquals("ArticulateArchive")); if (listNode == null) { throw new InvalidOperationException("An ArticulateArchive document must exist under the root Articulate document"); } var pager = new PagerModel(count, 0, 1); var listItems = helper.GetPostsSortedByPublishedDate(listNode.Id, pager); var rootPageModel = new ListModel(listNode, listItems, pager); return rootPageModel.Children<PostModel>(); }
public AuthorModel(IPublishedContent content, IEnumerable <IPublishedContent> listItems, PagerModel pager, int postCount) : base(content, listItems, pager) { PostCount = postCount; }
private ActionResult RenderByTagOrCategory(RenderModel model, int? p, string tagGroup, string baseUrl) { var tagPage = model.Content as ArticulateVirtualPage; if (tagPage == null) { throw new InvalidOperationException("The RenderModel.Content instance must be of type " + typeof(ArticulateVirtualPage)); } //create a blog model of the main page var rootPageModel = new ListModel(model.Content.Parent); var contentByTag = Umbraco.GetContentByTag(rootPageModel, tagPage.Name, tagGroup, baseUrl); //this is a special case in the event that a tag contains a '.', when this happens we change it to a '-' // when generating the URL. So if the above doesn't return any tags and the tag contains a '-', then we // will replace them with '.' and do the lookup again if (contentByTag == null && tagPage.Name.Contains("-")) { contentByTag = Umbraco.GetContentByTag( rootPageModel, tagPage.Name.Replace('-', '.'), tagGroup, baseUrl); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } //TODO: I wonder about the performance of this - when we end up with thousands of blog posts, // this will probably not be so efficient. I wonder if using an XPath lookup for batches of children // would work? The children count could be cached. I'd rather not put blog posts under 'month' nodes // just for the sake of performance. Hrm.... Examine possibly too. var totalPosts = contentByTag == null ? 0 : contentByTag.PostCount; var pageSize = rootPageModel.PageSize; var totalPages = totalPosts == 0 ? 1 : Convert.ToInt32(Math.Ceiling((double)totalPosts / pageSize)); //Invalid page, redirect without pages if (totalPages < p) { return new RedirectToUmbracoPageResult(model.Content.Parent, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "p=" + (p - 1) : p > 1 ? model.Content.Url : null); var listModel = new ListModel(tagPage, contentByTag == null ? Enumerable.Empty<PostModel>() : contentByTag.Posts, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }
public ListModel(IPublishedContent content, PagerModel pager) : base(content) { if (pager == null) throw new ArgumentNullException("pager"); _pager = pager; }
/// <summary> /// Used to render the search result listing (virtual node) /// </summary> /// <param name="model"></param> /// <param name="term"> /// The search term /// </param> /// <param name="provider"> /// The search provider name (optional) /// </param> /// <param name="p"></param> /// <returns></returns> public ActionResult Search(RenderModel model, string term, string provider = null, int? p = null) { var tagPage = model.Content as ArticulateVirtualPage; if (tagPage == null) { throw new InvalidOperationException("The RenderModel.Content instance must be of type " + typeof(ArticulateVirtualPage)); } //create a blog model of the main page var rootPageModel = new ListModel(model.Content.Parent); if (term == null) { //nothing to search, just render the view var emptyList = new ListModel(tagPage, Enumerable.Empty<IPublishedContent>(), new PagerModel(rootPageModel.PageSize, 0, 0)); return View(PathHelper.GetThemeViewPath(emptyList, "List"), emptyList); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } var searchResult = _articulateSearchPlugin.Search(term, provider, rootPageModel.BlogArchiveNode.Id); //TODO: I wonder about the performance of this - when we end up with thousands of blog posts, // this will probably not be so efficient. I wonder if using an XPath lookup for batches of children // would work? The children count could be cached. I'd rather not put blog posts under 'month' nodes // just for the sake of performance. Hrm.... Examine possibly too. var totalPosts = searchResult.Count(); var pageSize = rootPageModel.PageSize; var totalPages = totalPosts == 0 ? 1 : Convert.ToInt32(Math.Ceiling((double)totalPosts / pageSize)); //Invalid page, redirect without pages if (totalPages < p) { return new RedirectToUmbracoPageResult(model.Content.Parent, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p - 1) : p > 1 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term : null); var listModel = new ListModel(tagPage, searchResult, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }
/// <summary> /// Used to render the search result listing (virtual node) /// </summary> /// <param name="model"></param> /// <param name="term"> /// The search term /// </param> /// <param name="provider"> /// The search provider name (optional) /// </param> /// <param name="p"></param> /// <returns></returns> public ActionResult Search(RenderModel model, string term, string provider = null, int? p = null) { var tagPage = model.Content as ArticulateVirtualPage; if (tagPage == null) { throw new InvalidOperationException("The RenderModel.Content instance must be of type " + typeof(ArticulateVirtualPage)); } //create a blog model of the main page var rootPageModel = new MasterModel(model.Content.Parent); if (term == null) { //nothing to search, just render the view var emptyList = new ListModel(tagPage, Enumerable.Empty<IPublishedContent>(), new PagerModel(rootPageModel.PageSize, 0, 0)); return View(PathHelper.GetThemeViewPath(emptyList, "List"), emptyList); } if (p != null && p.Value == 1) { return new RedirectToUmbracoPageResult(model.Content, UmbracoContext); } if (p == null || p.Value <= 0) { p = 1; } int totalPosts; var searchResult = ArticulateSearcher.Search(term, provider, rootPageModel.BlogArchiveNode.Id, rootPageModel.PageSize, p.Value - 1, out totalPosts); var pageSize = rootPageModel.PageSize; var totalPages = totalPosts == 0 ? 1 : Convert.ToInt32(Math.Ceiling((double)totalPosts / pageSize)); //Invalid page, redirect without pages if (totalPages < p) { return new RedirectToUmbracoPageResult(model.Content.Parent, UmbracoContext); } var pager = new PagerModel( pageSize, p.Value - 1, totalPages, totalPages > p ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p + 1) : null, p > 2 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term + "&p=" + (p - 1) : p > 1 ? model.Content.Url.EnsureEndsWith('?') + "term=" + term : null); var listModel = new ListModel(tagPage, searchResult, pager); return View(PathHelper.GetThemeViewPath(listModel, "List"), listModel); }