diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.Blogs/AdminMenu.cs index 090905c5d..617a9baa9 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/AdminMenu.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/AdminMenu.cs @@ -41,7 +41,7 @@ namespace Orchard.Blogs { if (singleBlog != null) menu.Add(T("New Post"), "1.1", item => - item.Action("Create", "BlogPostAdmin", new {area = "Orchard.Blogs", blogId = singleBlog.Id}).Permission(Permissions.MetaListOwnBlogs)); + item.Action("Create", "BlogPostAdmin", new { area = "Orchard.Blogs", blogId = singleBlog.Id }).Permission(Permissions.MetaListOwnBlogs)); menu.Add(T("New Blog"), "1.2", item => diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/BlogPostLocalNavigationProvider.cs b/src/Orchard.Web/Modules/Orchard.Blogs/BlogPostLocalNavigationProvider.cs new file mode 100644 index 000000000..5e20fbdaa --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Blogs/BlogPostLocalNavigationProvider.cs @@ -0,0 +1,40 @@ +using Orchard.Blogs.Services; +using Orchard.Localization; +using Orchard.Security; +using Orchard.UI.Navigation; + +namespace Orchard.Blogs { + public class BlogPostsLocalNavigationProvider : INavigationProvider { + private readonly IBlogService _blogService; + private readonly IAuthorizationService _authorizationService; + private readonly IWorkContextAccessor _workContextAccessor; + + public BlogPostsLocalNavigationProvider( + IBlogService blogService, + IAuthorizationService authorizationService, + IWorkContextAccessor workContextAccessor) { + + T = NullLocalizer.Instance; + _blogService = blogService; + _authorizationService = authorizationService; + _workContextAccessor = workContextAccessor; + } + + public Localizer T { get; set; } + + public string MenuName { + get { return "blogposts-navigation"; } + } + + public void GetNavigation(NavigationBuilder builder) { + var blogId = 0; + int.TryParse(_workContextAccessor.GetContext().HttpContext.Request.RequestContext.RouteData.Values["blogId"]?.ToString(), out blogId); + if (blogId > 0) { + builder.Add(T("Blog posts"), + item => item.Action("Item", "BlogAdmin", new { area = "Orchard.Blogs", blogId }) + .LocalNav() + .Permission(Permissions.MetaListOwnBlogs)); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs index 001ef9fbf..796aa0c6e 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Web.Mvc; using Orchard.Blogs.Extensions; using Orchard.Blogs.Models; -using Orchard.Blogs.Routing; using Orchard.Blogs.Services; using Orchard.ContentManagement; using Orchard.Data; @@ -13,6 +12,7 @@ using Orchard.UI.Admin; using Orchard.UI.Navigation; using Orchard.UI.Notify; using Orchard.Settings; +using System.Collections.Generic; namespace Orchard.Blogs.Controllers { @@ -21,6 +21,7 @@ namespace Orchard.Blogs.Controllers { private readonly IBlogService _blogService; private readonly IBlogPostService _blogPostService; private readonly IContentManager _contentManager; + private readonly INavigationManager _navigationManager; private readonly ITransactionManager _transactionManager; private readonly ISiteService _siteService; @@ -29,6 +30,7 @@ namespace Orchard.Blogs.Controllers { IBlogService blogService, IBlogPostService blogPostService, IContentManager contentManager, + INavigationManager navigationManager, ITransactionManager transactionManager, ISiteService siteService, IShapeFactory shapeFactory) { @@ -36,6 +38,7 @@ namespace Orchard.Blogs.Controllers { _blogService = blogService; _blogPostService = blogPostService; _contentManager = contentManager; + _navigationManager = navigationManager; _transactionManager = transactionManager; _siteService = siteService; T = NullLocalizer.Instance; @@ -151,10 +154,10 @@ namespace Orchard.Blogs.Controllers { list.AddRange(_blogService.Get(VersionOptions.Latest) .Where(x => Services.Authorizer.Authorize(Permissions.MetaListOwnBlogs, x)) .Select(b => { - var blog = Services.ContentManager.BuildDisplay(b, "SummaryAdmin"); - blog.TotalPostCount = _blogPostService.PostCount(b, VersionOptions.Latest); - return blog; - })); + var blog = Services.ContentManager.BuildDisplay(b, "SummaryAdmin"); + blog.TotalPostCount = _blogPostService.PostCount(b, VersionOptions.Latest); + return blog; + })); var viewModel = Services.New.ViewModel() .ContentItems(list); @@ -179,6 +182,18 @@ namespace Orchard.Blogs.Controllers { var totalItemCount = _blogPostService.PostCount(blogPart, VersionOptions.Latest); blog.Content.Add(Shape.Pager(pager).TotalItemCount(totalItemCount), "Content:after"); + // Adds LocalMenus; + var menuItems = _navigationManager.BuildMenu("blogposts-navigation"); + var request = Services.WorkContext.HttpContext.Request; + + // Set the currently selected path + Stack selectedPath = NavigationHelper.SetSelectedPath(menuItems, request, request.RequestContext.RouteData); + + // Populate local nav + dynamic localMenuShape = Shape.LocalMenu().MenuName("local-admin"); + // NavigationHelper.PopulateLocalMenu(Shape, localMenuShape, localMenuShape, selectedPath); + NavigationHelper.PopulateLocalMenu(Shape, localMenuShape, localMenuShape, menuItems); + Services.WorkContext.Layout.LocalNavigation.Add(localMenuShape); return View(blog); } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj b/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj index ce1cda30c..b28fc95df 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj @@ -95,6 +95,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml b/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml index 6534c6b2f..76581b42c 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml @@ -1,5 +1,5 @@ @{ Layout.Title = T("Manage Blog").ToString(); } - @* Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type *@ - @Display(Model) +@* Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type *@ +@Display(Model) diff --git a/src/Orchard.Web/Modules/Orchard.Search/BlogPostsLocalNavigationProvider.cs b/src/Orchard.Web/Modules/Orchard.Search/BlogPostsLocalNavigationProvider.cs new file mode 100644 index 000000000..03acc6638 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Search/BlogPostsLocalNavigationProvider.cs @@ -0,0 +1,42 @@ +using System.Linq; +using Orchard.Blogs; +using Orchard.Blogs.Services; +using Orchard.Environment.Extensions; +using Orchard.Localization; +using Orchard.Security; +using Orchard.UI.Navigation; + +namespace Orchard.Search { + [OrchardFeature("Orchard.Search.Blogs")] + public class BlogPostsLocalNavigationProvider : INavigationProvider { + private readonly IBlogService _blogService; + private readonly IAuthorizationService _authorizationService; + private readonly IWorkContextAccessor _workContextAccessor; + + public BlogPostsLocalNavigationProvider( + IBlogService blogService, + IAuthorizationService authorizationService, + IWorkContextAccessor workContextAccessor) { + + T = NullLocalizer.Instance; + _blogService = blogService; + _authorizationService = authorizationService; + _workContextAccessor = workContextAccessor; + } + + public Localizer T { get; set; } + + public string MenuName { + get { return "blogposts-navigation"; } + } + public void GetNavigation(NavigationBuilder builder) { + var blogId = 0; + int.TryParse(_workContextAccessor.GetContext().HttpContext.Request.RequestContext.RouteData.Values["blogId"]?.ToString(), out blogId); + if (blogId > 0) { + builder.Add(T("Search Posts"), "2.0", item => item.Action("Index", "BlogSearch", new { area = "Orchard.Search", blogId }) + .LocalNav() + .Permission(Permissions.MetaListOwnBlogs)); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Search/Constants.cs b/src/Orchard.Web/Modules/Orchard.Search/Constants.cs new file mode 100644 index 000000000..696a300ca --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Search/Constants.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Orchard.Environment.Extensions; + +namespace Orchard.Search { + [OrchardFeature("Orchard.Search.Blogs")] + public class BlogSearchConstants { + public static string ADMIN_BLOGPOSTS_INDEX = "AdminBlogPosts"; + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Search/Controllers/BlogSearchController.cs b/src/Orchard.Web/Modules/Orchard.Search/Controllers/BlogSearchController.cs new file mode 100644 index 000000000..6497eaa9b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Search/Controllers/BlogSearchController.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Orchard.Collections; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData; +using Orchard.DisplayManagement; +using Orchard.Environment.Extensions; +using Orchard.Indexing; +using Orchard.Localization; +using Orchard.Localization.Services; +using Orchard.Logging; +using Orchard.Search.Helpers; +using Orchard.Search.Models; +using Orchard.Search.Services; +using Orchard.Security; +using Orchard.Settings; +using Orchard.UI.Admin; +using Orchard.UI.Navigation; +using Orchard.UI.Notify; + +namespace Orchard.Search.Controllers { + [OrchardFeature("Orchard.Search.Blogs")] + [Admin] + public class BlogSearchController : Controller { + private readonly ISearchService _searchService; + private readonly ISiteService _siteService; + private readonly IIndexManager _indexManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IAuthorizer _authorizer; + private readonly ICultureManager _cultureManager; + private readonly INavigationManager _navigationManager; + + public BlogSearchController( + IOrchardServices orchardServices, + ISearchService searchService, + ISiteService siteService, + IIndexManager indexManager, + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + IAuthorizer authorizer, + ICultureManager cultureManager, + INavigationManager navigationManager, + IShapeFactory shapeFactory) { + + _searchService = searchService; + _siteService = siteService; + Services = orchardServices; + _indexManager = indexManager; + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _authorizer = authorizer; + _cultureManager = cultureManager; + _navigationManager = navigationManager; + Shape = shapeFactory; + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; + } + + public IOrchardServices Services { get; set; } + public ILogger Logger { get; set; } + public Localizer T { get; set; } + public dynamic Shape { get; set; } + + public ActionResult Index(int blogId, PagerParameters pagerParameters, string searchText = "") { + var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); + var searchSettingsPart = Services.WorkContext.CurrentSite.As(); + + IPageOfItems searchHits = new PageOfItems(new ISearchHit[] { }); + try { + if (!string.IsNullOrWhiteSpace(searchText)) { + var searchableTypes = new List(); + // add the type to the list of types we will filter for + // BlogPost for now but we would add more types in the future (i.e. "Article") + searchableTypes.Add("BlogPost"); + var searchBuilder = _indexManager.HasIndexProvider() + ? _indexManager + .GetSearchIndexProvider() + .CreateSearchBuilder(BlogSearchConstants.ADMIN_BLOGPOSTS_INDEX) + : new NullSearchBuilder(); + + searchBuilder + .Parse(searchSettingsPart + .GetSearchFields(BlogSearchConstants.ADMIN_BLOGPOSTS_INDEX), + searchText); + + // filter by Blog + searchBuilder + .WithField("container-id", blogId) + .Mandatory() + .NotAnalyzed() + .AsFilter(); + + foreach (var searchableType in searchableTypes) { + // filter by type + searchBuilder + .WithField("type", searchableType) + .NotAnalyzed() + .AsFilter(); + } + // pagination + var totalCount = searchBuilder.Count(); + if (pager != null) { + searchBuilder = searchBuilder + .Slice( + (pager.Page > 0 ? pager.Page - 1 : 0) * pager.PageSize, + pager.PageSize); + } + // search + var searchResults = searchBuilder.Search(); + // prepare the shape for the page + searchHits = new PageOfItems(searchResults.Select(searchHit => searchHit)) { + PageNumber = pager != null ? pager.Page : 0, + PageSize = pager != null ? (pager.PageSize != 0 ? pager.PageSize : totalCount) : totalCount, + TotalItemCount = totalCount + }; + } + + } + catch (Exception exception) { + Logger.Error(T("Invalid search query: {0}", exception.Message).Text); + Services.Notifier.Error(T("Invalid search query: {0}", exception.Message)); + } + + var list = Services.New.List(); + foreach (var contentItem in Services.ContentManager.GetMany(searchHits.Select(x => x.ContentItemId), VersionOptions.Latest, QueryHints.Empty)) { + // ignore search results which content item has been removed + if (contentItem == null) { + searchHits.TotalItemCount--; + continue; + } + + list.Add(Services.ContentManager.BuildDisplay(contentItem, "SummaryAdmin")); + } + + var pagerShape = Services.New.Pager(pager).TotalItemCount(searchHits.TotalItemCount); + + var viewModel = Services.New.ViewModel() + .ContentItems(list) + .Pager(pagerShape) + .SearchText(searchText) + .BlogId(blogId); + + // Adds LocalMenus; + var menuItems = _navigationManager.BuildMenu("blogposts-navigation"); + var request = Services.WorkContext.HttpContext.Request; + + // Set the currently selected path + Stack selectedPath = NavigationHelper.SetSelectedPath(menuItems, request, request.RequestContext.RouteData); + + // Populate local nav + dynamic localMenuShape = Shape.LocalMenu().MenuName("local-admin"); + + NavigationHelper.PopulateLocalMenu(Shape, localMenuShape, localMenuShape, menuItems); + Services.WorkContext.Layout.LocalNavigation.Add(localMenuShape); + + return View(viewModel); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Search/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Search/Migrations.cs index a680469c0..f03156f00 100644 --- a/src/Orchard.Web/Modules/Orchard.Search/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Search/Migrations.cs @@ -69,4 +69,23 @@ namespace Orchard.Search { return 1; } } + + [OrchardFeature("Orchard.Search.Blogs")] + public class BlogsMigration : DataMigrationImpl { + private readonly IIndexManager _indexManager; + + public BlogsMigration(IIndexManager indexManager) { + _indexManager = indexManager; + } + + public int Create() { + + _indexManager.GetSearchIndexProvider().CreateIndex(BlogSearchConstants.ADMIN_BLOGPOSTS_INDEX); + + ContentDefinitionManager.AlterTypeDefinition("BlogPost", cfg => cfg.WithSetting("TypeIndexing.Indexes", BlogSearchConstants.ADMIN_BLOGPOSTS_INDEX + ":latest")); + + return 1; + } + } + } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Search/Module.txt b/src/Orchard.Web/Modules/Orchard.Search/Module.txt index ccbce4fc6..c40d8c5ad 100644 --- a/src/Orchard.Web/Modules/Orchard.Search/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Search/Module.txt @@ -26,4 +26,9 @@ Features: Name: Media Library Search Description: Provides search menu item in the Media Library explorer. Dependencies: Orchard.MediaLibrary, Orchard.Search + Category: Search + Orchard.Search.Blogs: + Name: Blog posts Search + Description: Provides search menu item in the Blog section. + Dependencies: Orchard.Blogs, Orchard.Search Category: Search \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj b/src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj index c992378c0..8c1fb4f02 100644 --- a/src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj +++ b/src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj @@ -93,7 +93,10 @@ + + + @@ -134,6 +137,10 @@ Orchard.Core $(MvcBuildViews) + + {63FBD4D9-E1DA-4A7B-AA6A-D6074FE50867} + Orchard.Blogs + {73a7688a-5bd3-4f7e-adfa-ce36c5a10e3b} Orchard.MediaLibrary @@ -207,6 +214,7 @@ + 10.0 @@ -246,4 +254,4 @@ - + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Search/Routes.cs b/src/Orchard.Web/Modules/Orchard.Search/Routes.cs index 4cd35d055..738ec9af7 100644 --- a/src/Orchard.Web/Modules/Orchard.Search/Routes.cs +++ b/src/Orchard.Web/Modules/Orchard.Search/Routes.cs @@ -41,6 +41,21 @@ namespace Orchard.Search { {"area", "Orchard.Search"} }, new MvcRouteHandler()) + }, + new RouteDescriptor { + Priority = 5, + Route = new Route("Admin/Search/BlogSearch/{blogId}", + new RouteValueDictionary { + {"area", "Orchard.Search"}, + {"controller", "BlogSearch"}, + {"action", "Index"}, + {"blogId", UrlParameter.Optional} + }, + null, + new RouteValueDictionary { + {"area", "Orchard.Search"} + }, + new MvcRouteHandler()) } }; } diff --git a/src/Orchard.Web/Modules/Orchard.Search/Views/BlogSearch/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Search/Views/BlogSearch/Index.cshtml new file mode 100644 index 000000000..e0a9b88de --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Search/Views/BlogSearch/Index.cshtml @@ -0,0 +1,21 @@ +@{ + var pageTitle = T("Search Content"); + Layout.Title = pageTitle; +} + +@using (Html.BeginFormAntiForgeryPost(Url.Action("index", new { controller = "BlogSearch", area = "Orchard.Search", blogId = Model.BlogId }), FormMethod.Get)) { + + @Html.TextBox("searchText", (string)Model.SearchText, new { @class = "text medium", autofocus = "autofocus" }) + + + +
+ @Display(Model.ContentItems) +
+ + if (HasText(Model.SearchText) && Model.ContentItems.Items.Count == 0) { + @T("There are no results") + } + + @Display(Model.Pager) +} \ No newline at end of file diff --git a/src/Orchard.Web/Themes/TheAdmin/Views/LocalMenu.cshtml b/src/Orchard.Web/Themes/TheAdmin/Views/LocalMenu.cshtml index 87dc8244d..efdc9129b 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Views/LocalMenu.cshtml +++ b/src/Orchard.Web/Themes/TheAdmin/Views/LocalMenu.cshtml @@ -6,8 +6,9 @@ Model.Attributes.Add("role", "local-navigation"); var tag = Tag(Model, "ul"); } -@tag.StartElement - @foreach(var firstLevelMenuItem in Model) { +@if (Model.Items!=null && Model.Items.Count > 0) { + @tag.StartElement + foreach (var firstLevelMenuItem in Model) { if (firstLevelMenuItem.LocalNav) { string sectionHeaderText = firstLevelMenuItem.Text.Text; @@ -34,8 +35,9 @@ firstLevelMenuItem.Classes.Add("local-section-" + sectionHeaderText.HtmlClassify()); var firstLevelTag = Tag(firstLevelMenuItem, "li"); @firstLevelTag.StartElement - @sectionHeaderMarkup - @firstLevelTag.EndElement + @sectionHeaderMarkup + @firstLevelTag.EndElement } } -@tag.EndElement + @tag.EndElement +} \ No newline at end of file