Set PageSize in a Taxonomy view - orchardcms

I've started my first project using OrchardCMS and so far so good. It's a brilliant piece of kit and amazingly powerful once you get up to speed with the terminology of it.
One area I'm struggling with is displaying Taxonomies. There seems to be almost no documentation on it, and Shape Tracing is not being very useful.
I'm using a taxonomy to display content items that have the same term. This is all working great now, however the default pagination page size is 10 items. I've dug through lots of code and found this is driven from TermPart
protected override DriverResult Display(TermPart part, string displayType, dynamic shapeHelper) {
return Combined(
ContentShape("Parts_TermPart_Feed", () => {
// generates a link to the RSS feed for this term
_feedManager.Register(part.Name, "rss", new RouteValueDictionary { { "term", part.Id } });
return null;
}),
ContentShape("Parts_TermPart", () => {
var pagerParameters = new PagerParameters();
var httpContext = _httpContextAccessor.Current();
if (httpContext != null) {
pagerParameters.Page = Convert.ToInt32(httpContext.Request.QueryString["page"]);
}
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var taxonomy = _taxonomyService.GetTaxonomy(part.TaxonomyId);
var totalItemCount = _taxonomyService.GetContentItemsCount(part);
var partSettings = part.Settings.GetModel<TermPartSettings>();
if (partSettings != null && partSettings.OverrideDefaultPagination) {
pager.PageSize = partSettings.PageSize;
}
The question is where is TermPart & TermPartSettings defined?
Thanks in advance

The TermPartSettings are in the Modules/Orchard.Taxonomies/Settings folder. The TermPart itself is in the Modules/Orchard.Taxonomies/Models directory. Don't know what you want with that information though, because you shouldnt edit an Orchard's module code.
When you create a new Taxonomy in the dashboard of Orchard, a new content type is created under Content Definition. It is called YourTaxonomy Term. This content type has a setting called 'override the default page size', where you can define your own page size.

Related

How to set different page size for first page in Orchard CMS

We got the need to display a blog posts page that display X posts - first post is displayed as a header and the rest are in 2 columns. The page has a show more button at the bottom that fetches the next page posts using ajax and adding them at the bottom.
Is it possible to get X+1 items for the subsequent pages?
Any hint, even in code are welcome since we use a sourced version of orchard installation.
So before cluttering the comments above this is my proposed solution.
I think there was a slight misunderstanding about changing the controller action which I'd like to clarify (I hope I understood everything correctly now):
Orchard.Blogs | BlogController | Item Action
public ActionResult Item(int blogId, PagerParameters pagerParameters) {
// This is all original code
Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var blogPart = _blogService.Get(blogId, VersionOptions.Published).As<BlogPart>();
if (blogPart == null)
return HttpNotFound();
if (!_services.Authorizer.Authorize(Orchard.Core.Contents.Permissions.ViewContent, blogPart, T("Cannot view content"))) {
return new HttpUnauthorizedResult();
}
// This is the actual change:
// Use the pagerParameters provided, otherwise fall back to the blog settings
pager.PageSize = pagerParameters.PageSize.HasValue ? pager.PageSize : blogPart.PostsPerPage;
// This is all original code again
_feedManager.Register(blogPart, _services.ContentManager.GetItemMetadata(blogPart).DisplayText);
var blogPosts = _blogPostService.Get(blogPart, pager.GetStartIndex(), pager.PageSize) // Your new page size will be used
.Select(b => _services.ContentManager.BuildDisplay(b, "Summary"));
dynamic blog = _services.ContentManager.BuildDisplay(blogPart);
var list = Shape.List();
list.AddRange(blogPosts);
blog.Content.Add(Shape.Parts_Blogs_BlogPost_List(ContentItems: list), "5");
var totalItemCount = _blogPostService.PostCount(blogPart);
blog.Content.Add(Shape.Pager(pager).TotalItemCount(totalItemCount), "Content:after");
return new ShapeResult(this, blog);
}
So the change is very subtle, but this way I would configure the blogs default pageSize to 7 items and for every subsequent Ajax-Request I'd provide a "pageSize"-Parameter with the desired size.

null ContentItem in Orchard

First - disclaimer: I know I should not do it like this, but use LazyField instead and that model should not contain logic and I will modify my code accordingly, but I wanted to explore the 1-n relationship between content items and orchard in general.
I'm creating a system where user can respond to selected job offer, so I have two content types - Job, which lists all available jobs, and JobAnswer which contains my custom part and provides link to appropriate Job content item:
public class JobPart : ContentPart<JobPartRecord>
{
public ContentItem Job
{
get
{
if (Record.ContentItemRecord != null)
{
var contentItem = ContentItem.ContentManager.Get(Record.Job.Id);
return contentItem;
}
var nullItem = ContentItem.ContentManager.Query("Job").List().First();
return nullItem;
}
set { Record.Job = value.Record; }
}
}
This works, but I'm not sure how should I handle returning a null contentItem, when creating new content item, now it just returns first Job content item, which is far from ideal.

Trying to hide Content Parts on Content Type

I am building Content Types and adding Content Parts specific to a Client and Attorney. All of these parts have fields and/or content pickers, etc.
I want to restrict the Client Role to see only Client Content Parts, while I just allow the Attorney Role to see any Content Parts, including it's own Attorney Content Part for a particular Content Type. Again, these are all on the same Content Type, so Content Permissions will not work (except on the Content Type in general).
I want to hide the Attorney Content Parts when a Client is logged on.
I have tried using this:
public override void Displaying(ShapeDisplayingContext context)
{
context.ShapeMetadata.OnDisplaying(displayedContext => {
var shape = context.Shape;
if (context.Shape.Part.Name == "Parts_AttorneyMatterPart")
{
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
var roles = user.As<UserRolesPart>().Roles;
if (!roles.Contains("Spaces Attorney"))
{
shape = null;
}
}
});
}
Where I have a Content Part named "AttorneyMatterPart", and where the Attorney Role is "Spaces Attorney".
These Content Types and Parts were all created in the Orchard Admin. The only thing in my module is this class file.
But this won't hide the Content Part when the Client is logged in. I know that I have to work on the logic of what roles can see things (going to add || conditions for Admin, etc.). For now I am just testing this out.
Any help is appreciated.
EDIT (Bounty Added)
I am really stumped as to whether or not this is even possible. This Content Part is created through the Admin UI. Under shape tracing I can see under the "Content" zone Model > ContentItem > AttorneyMatterPart. I have tried ShapeTableBuilder and I have tried OnDisplaying and OnDisplayed from a ShapeDisplayingContext.
If someone could provide a working sample it would be much appreciated.
When a content part is created through the admin dashboard, there isn't really a shape to render it, only individual shapes for inner content fields...
So, try this
public override void Displaying(ShapeDisplayingContext context) {
context.ShapeMetadata.OnDisplaying(displayedContext => {
var shape = displayedContext.Shape;
if (shape.ContentPart != null
&& shape.ContentPart.PartDefinition.Name == "PartName") {
var workContext = _workContextAccessor.GetContext();
var user = workContext.CurrentUser;
if (user == null || !user.Has<UserRolesPart>()
|| !user.As<UserRolesPart>().Roles.Contains("RoleName")) {
displayedContext.ChildContent = new System.Web.HtmlString("");
}
}
});
}
See my answer on OrchardPros
http://orchardpros.net/tickets/6914
Best
Nulling the shape variable will just clear the local reference. Setting the following however should hide the shape:
displayedContext.ShapeMetadata.Position = "-";
Also FYI it's better not to check on roles the user have but rather create a custom permission, add that to the user role and then check for the permission through
IAuthorizationService.TryCheckAccess()

Welding a ContentPart having a ContentField

I'm trying to Weld my custom ContentPart SitesPart containing a ContentField of type TaxonomyField but it is not working for me. When i attach this part from UI it works perfectly fine and i see the TaxonomyField in edit mode as well as in display mode.
Following is the Activating method of my ContentHandler.
protected override void Activating(ActivatingContentContext context)
{
if (context.ContentType == "Page")
{
context.Builder.Weld<SitesPart>();
}
}
I tried to go deep into the Weld function and found out that it is not able to find correct typePartDefinition. It goes inside the condition if (typePartDefinition == null) which creates an empty typePartDefinition with no existing ContentFields etc.
// obtain the type definition for the part
var typePartDefinition = _definition.Parts.FirstOrDefault(p => p.PartDefinition.Name == partName);
if (typePartDefinition == null) {
// If the content item's type definition does not define the part; use an empty type definition.
typePartDefinition =
new ContentTypePartDefinition(
new ContentPartDefinition(partName),
new SettingsDictionary());
}
I would be highly thankful for any guidance.
Oh, you are totally right, the part is welded but if there are some content fields, they are not welded. The ContentItemBuilder try to retrieve the part definition through the content type definition on which we want to add the part. So, because it's not possible, a new content part is created but with an empty collection of ContentPartFieldDefinition...
I think that the ContentItemBuilder would need to inject in its constructor and use a ContentPartDefinition or more generally an IContentDefinitionManager... But, for a quick workaround I've tried the following that works
In ContentItemBuilder.cs, replace this
public ContentItemBuilder Weld<TPart>()...
With
public ContentItemBuilder Weld<TPart>(ContentPartDefinition contentPartDefinition = null)...
And this
new ContentPartDefinition(partName),
With
contentPartDefinition ?? new ContentPartDefinition(partName),
And in you part handler, inject an IContentDefinitionManager and use this
protected override void Activating(ActivatingContentContext context) {
if (context.ContentType == "TypeTest") {
var contentPartDefinition = _contentDefinitionManager.GetPartDefinition(typeof(FruitPart).Name);
context.Builder.Weld<FruitPart>(contentPartDefinition);
}
}
Best
To attach a content part to a content type on the fly, you can use this in your handler
Filters.Add(new ActivatingFilter<YourContentPart>("YourContentType"));
There are many examples in the source code
Best

Using FindView in Orchard

I'm trying to use:
var viewEngineResult = ViewEngines.Engines.FindView(ControllerContext, myViewName, null);
as part of a process to render the contents of a view to send nice formatted emails. I'm using it inside an Orchard Controller. I have used similar code outside of Orchard in an MVC project and it works fine.
However in Orchard running this code fails to find the view I'm looking for and returns a view engine result that has searched 0 locations.
viewEngineResult has the following values after it is called:
SearchedLocations: Count = 0,
View: null,
ViewEngine: null
Is there a reason this doesn't work in Orchard and is there a way to make it work?
This answer is based on the advise given me by Bertrand, but I wanted to bring it together with what I'd discovered.
To be able to use FindPartialView I needed to inject an instance of IViewEngineProvider into my controller.
I then used the following code to resolve and render a view:
private String RenderView(String viewName, object model)
{
var paths = new List<string>(); // This can just be an empty list and it still finds it.
var viewEngine = _viewEngineProvider.CreateModulesViewEngine(new CreateModulesViewEngineParams {VirtualPaths = paths});
var viewResult = viewEngine.FindPartialView(ControllerContext, viewName, false);
if (viewResult.View == null) {
throw new Exception("Couldn't find view " + viewName);
}
var viewData = new ViewDataDictionary {Model = model};
using (var sw = new StringWriter())
{
var viewContext = new ViewContext(ControllerContext, viewResult.View, viewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
I think you'll want to take a close look at Orchard.Framework/Mvc/ViewEngines, in particular IViewEngineProvider and ThemeAwareViewEngine. There's a lot more going on when in Orchard, such as themes, multi-tenancy, and a richer environment in general that may be needed to make this work.
What's likely happening here is that the view engines don't have enough information to resolve a view and thus opt out of the chain. You might want to put a breakpoint into ThemeAwareViewEngine.FindView, and then inspect the private dependency fields of that class. I wouldn't be surprised if they were null, because getting to FindView through statics will probably not allow dependency injection to do its stuff properly.
Then again I'm just guessing.

Resources