Orchard - link to a view/action in another module? - orchardcms

Say I am on Module1 -> Controller1 -> View1.
In View1, I want to add a regular hyperlink to Module2 -> Controller2 -> View2.
How do I do that?
Thanks.

In ASP.NET MVC links point to action on a controller, not to a view. View can (but not have to be) returned from a given action as a result.
So, if your View2 is returned from eg. action MyAction on Controller2 sitting in Module2, then you can easily add a link to that action from anywhere by writing:
#Html.ActionLink("Click Me",
"MyAction",
"Controller2",
new { area = "Module2" },
new {})
or
#Html.ActionLink("Click Me",
"MyAction",
new { controller = "Controller2", area = "Module2" })
whichever looks better for you. There are a couple of other possible overrides.
The Area route property is used in Orchard to specify the target module name.

Related

How to add an absolute element in a NativeScript page

I want to be able to just place a View component (plugin) into the page through code and have it appear at some X\Y on the page... but I'm a bit stumped.
Any attempt to add via page.content kinda adds it to the layout\render pass so it occupies space.
So this would get injected into "any" page at "any" time, I have no control over the markup this would be used in (know what I mean?) There is no XML for it and unfortunately the answer can't just be wrap everything in an AbsoluteLayout because one can't mandate that on users apps\layouts.
Thoughts, even possible?
Basically the simplest way to do this is to dynamically and be fully cross platform compatible is to create a AbsoluteLayout item in your JavaScript code, and dynamically insert your item and the AL into the page container.
Code would be something like this:
var AbsoluteLayout = require('ui/layouts/absolute-layout').AbsoluteLayout;
var myAL = new AbsoluteLayout();
var myItem = new myPluginItem();
// Set you left, right, top, bottom coords.
myItem.top = x;
// Add our item to the AbsoluteItem
myAL.addChild(myItem);
var frame = require('ui/frame');
var page = frame.topmost().currentPage;
var LayoutBase = require('ui/layouts/layout-base').LayoutBase;
page._eachChildView(function(view) {
if (view instanceof LayoutBase) {
view.addChild(myAL);
return false;
}
return true;
});
However, if you don't want to do this the really simple way; the only other way is to actually go a bit lower level. You can natively access the iOS view controller (page._ios.view) and the android view (page._nativeView), and then manually add it to the view by using things like addView (https://developer.android.com/reference/android/view/ViewManager.html) or addSubview (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/).
I would like to add you can set the Top and Left properties in TypeScript by importing AbsoluteLayout like so
import {AbsoluteLayout} from 'ui/layouts/absolute-layout';
and then using the exposed functions setLeft or setTop
AbsoluteLayout.setLeft(YourItem, LeftValue);
or
AbsoluteLayout.setTop(YourItem, TopValue);

Orchard CMS front-end all possible content filtering by user permissions

Good day!
In my Orchard, I have several content types all with my custom part attached. This part defines to what users this content is available. For each logged user there is external service, which defines what content user can or cannot access. Now I need access restriction to apply everywhere where orchard display content lists, this includes results by specific tag from a tag cloud, or results listed from Taxonomy term. I seems can’t find any good way to do it except modifying TaxonomyServices code as well as TagCloud services, to join also my part and filter by it. Is this indeed the only way to do it or there are other solutions? I would like to avoid doing changes to built-in modules if possible but cannot find other way.
Thanks in advance.
I'm currently bumbling around with the same issue. One way I'm currently looking at is to hook into the content manager.
[OrchardSuppressDependency("Orchard.ContentManagement.DefaultContentManager")]
public class ModContentManager : DefaultContentManager, IContentManager
{
//private readonly Lazy<IShapeFactory> _shapeFactory;
private readonly IModAuthContext _modAuthContext;
public ModContentManager(IComponentContext context,
IRepository<ContentTypeRecord> contentTypeRepository,
IRepository<ContentItemRecord> contentItemRepository,
IRepository<ContentItemVersionRecord> contentItemVersionRepository,
IContentDefinitionManager contentDefinitionManager,
ICacheManager cacheManager,
Func<IContentManagerSession> contentManagerSession,
Lazy<IContentDisplay> contentDisplay,
Lazy<ISessionLocator> sessionLocator,
Lazy<IEnumerable<IContentHandler>> handlers,
Lazy<IEnumerable<IIdentityResolverSelector>> identityResolverSelectors,
Lazy<IEnumerable<ISqlStatementProvider>> sqlStatementProviders,
ShellSettings shellSettings,
ISignals signals,
//Lazy<IShapeFactory> shapeFactory,
IModAuthContext modAuthContext)
: base(context,
contentTypeRepository,
contentItemRepository,
contentItemVersionRepository,
contentDefinitionManager,
cacheManager,
contentManagerSession,
contentDisplay,
sessionLocator,
handlers,
identityResolverSelectors,
sqlStatementProviders,
shellSettings,
signals) {
//_shapeFactory = shapeFactory;
_modAuthContext = modAuthContext;
}
public new dynamic BuildDisplay(IContent content, string displayType = "", string groupId = "") {
// So you could do something like...
// var myPart = content.As<MyAuthoPart>();
// if(!myPart.IsUserAuthorized)...
// then display something else or display nothing (I think returning null works for this but
//don't quote me on that. Can always return a random empty shape)
// else return base.BuildDisplay(content, displayType, groupId);
// ever want to display a shape based on the name...
//dynamic shapes = _shapeFactory.Value;
}
}
}
Could also hook into the IAuthorizationServiceEventHandler, which is activated before in the main ItemController and do a check to see if you are rendering a projection or taxonomy list set a value to tell your content manager to perform checks else just let them through. Might help :)

MVC5 Rewriting Routing Attribute - Default page

I am attempting to switch from RouteConfig to Routing Attributes.
I am following along the Pro ASP.NET MVC 5 book from Adam Freeman and I'm trying to convert the following code that handles the paging of clients.
routes.MapRoute(
name: null,
url: "{controller}/Page{page}",
defaults: new { action = "Index", status = (string)null },
constraints: new { page = #"\d+" }
);
This works great! As I go to different URLs, the links look very nice
http://localhost:65534/Client - Default page
http://localhost:65534/Client/Page2 - Second page
Now I've decided to try out Url Attributes and having a bit of problems when it comes to how 'pretty' the links are. All of the links are working fine, but it's the 'routing rewriting' that I am trying to fix.
Here are the important parts of my controller.
[RoutePrefix("Client")]
[Route("{action=index}/{id:int?}")]
public class ClientController : Controller {
[Route("Page{page:int?}")]
public ActionResult Index(string sortOrder, string search = null, int page = 1) {
With the attribute above the Index, going to /Client or to /Client/Page gives me a 404.
Adding a blank route to catch the default page
[Route("Page{page:int?}")]
[Route]
Works for /Client and /Client/Page3, but now the rewriting of the URL is messed up. Clicking on page 3 of the pager gives me a URL of
http://localhost:65534/Client?page=3
which is not what I want. Changing the routing to
[Route("Page{page:int?}")]
[Route("{page=1:int?}")]
Works almost 100%, but the default link for /Client is now
http://localhost:65534/Client/Page
So, I am now asking for help. How can I correctly convert the original MapRoute to the attributes?
Just use:
[Route("", Order = 1)]
[Route("Page{page:int}", Order = 2)]
UPDATE
Plainly and simply, the routing framework is dumb. It doesn't make decisions about which route is the most appropriate, it merely finds a matching route and returns. If you do something like:
Url.Action("Index", "Client", new { page = 1 })
You're expecting the generated URL to be /Client/Page1, but since you have a route where page is essentially optional, it always will choose that route and append anything it can't stuff into the URL as a querystring, i.e. /Client?page=1. The only way to get around this is to actually name the route you want and use that named route to generate the URL. For example:
[Route("", Order = 1)]
[Route("Page{page:int}", Name = "ClientWithPage", Order = 2)]
And then:
Url.RouteUrl("ClientWithPage", new { page = 1 })
Then, you'll get the route you expect because you're directly referencing it.
UPDATE #2
I'm not sure what you mean by "go into PagedList.MVC and add a name property to it". It doesn't require any core changes to the code because PagedList already has support for custom page links. Just change your pager code to something like:
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfItems, page => Url.RouteUrl("ClientWithPage", new { page = page }))
And you'll get the URL style you want. Attribute routing can be a bit more finicky than traditional routing, but I'd hardly call it useless. It's far more flexible than traditional routing, but that flexibility has some costs.

EXT.Net - Dynamic UserControlLoader with Property

I need a solution to the below scenario. I'm developing an application in ASP.Net with the help of EXT.Net Controls.
In my scenario, I'm creating dynamic tabs (EXT.Net) and loading an User Control dynamically with UserControlLoader component.
How can I pass a parameter to the UserControl dynamically? Below is my sample code.
[DirectMethod]
public void AddNewTab()
{
getTitle gt = new getTitle();
Ext.Net.Panel panel = new Ext.Net.Panel
{
Title = gt.Title(),
Closable = false,
Layout = "Fit",
Items = {
new UserControlLoader{
Path="ElementChooser.ascx"
}
}
};
TabPanel1.Add(panel);
panel.Render();
TabPanel1.SetLastTabAsActive();
}
Your help is highly appreciated.
It is a bit unclear what "pass a parameter to a User Control" means, but I think you need to handle the UserControlLoader's OnUserControlAdded and OnComponentAdded events.
The first one fires when a user control is added to a UserControlLoader.
The second one fires for each top level Ext.NET component from a user control.
These events helps to configure a user control and its components as needed.
Here is an example with the OnComponentAdded example.

How to set new Orchard module to be Home Page via code

I'm very new with orchard.
To learn orchard module development, I followed the documentation and tried to create a commerce module.
The module consists of product part and product type which has product part.
During enable module, it will create admin and home menu for this module, "Commerce" and "Shop" respectively.
My questions are
How do I make this module to be home page during enable module. In other word, I want Index method of
the module's HomeController handle home url?
How do I get Shop menu in front end to be after home menu or register this module to home menu?
I am attaching source code, please download it from the following link
download source code
To take over the home page the standard Orchard way is to implement IHomePageProvider.
You can, when creating a page as part of migrations.cs in a module, tell the Autoroute part to set your created page's alias as the homepage:
//create a page page
var homepage = _contentManager.Create("Page");
homepage.As<TitlePart>().Title = "My Home";
_contentManager.Publish(homepage);
var homePageArp = homepage.As<AutoroutePart>();
homePageArp.DisplayAlias = String.Empty;
_autorouteService.PublishAlias(homePageArp);
This assumes you're going from a clean instance of Orchard without any prior homepages; if you have an existing homepage, you'll have to regenerate those pages' Aliases as part of your module too. This is how it's done as part of the AutoroutePartHandler in the Orchard.Autoroute project (inside the Publish Alias method):
// regenerate the alias for the previous home page
var currentHomePages = _orchardServices.ContentManager.Query<AutoroutePart, AutoroutePartRecord>().Where(x => x.DisplayAlias == "").List();
foreach (var current in currentHomePages) {
if (current != null) {
current.CustomPattern = String.Empty; // force the regeneration
current.DisplayAlias = _autorouteService.Value.GenerateAlias(current);
}
_autorouteService.Value.PublishAlias(current);
}
_autorouteService.Value.PublishAlias(part);
If you dig through the driver and handler for the autoroute project, you'll learn a lot about the internals; when you tick that "set as homepage" box in the Admin UI, it sets the Path to "/" and then that gets picked up, triggers the old homepage re-wire, clears the "/" path to String.Empty and then publishes that blank alias, giving you a new homepage.
(this is valid as of Orchard 1.6)
If your module is to be used by others, then it is better to make a widget which can be added to any layer (the homepage layer for example). That way each user can decide where your module comes into play.
If you are using this module for yourself only, then you can just override the default routes (standard mvc functionallity).
Look at my ExtendedRegistration module (Routes.cs) to see how it's done.
Here I am overriding the standard Account/Register URL. There should be nothing preventing you from overriding the default HomeController.
public class Routes : IRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
foreach (var routeDescriptor in GetRoutes())
{
routes.Add(routeDescriptor);
}
}
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new RouteDescriptor {
Priority = 19,
Route = new Route(
"Users/Account/Register",
new RouteValueDictionary {
{"area", "itWORKS.ExtendedRegistration"},
{"controller", "Account"},
{"action", "Register"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "itWORKS.ExtendedRegistration"}
},
new MvcRouteHandler())
}
};
}
}

Resources