Kentico 11 customapi MapHttpRoute - kentico

I am looking for a solution in Kentico 11 to add the mapping of our custom API:
GlobalConfiguration.Configuration.Routes.MapHttpRoute ("customapi", "customapi / {controller} / {id}", new {id = System.Web.Http.RouteParameter.Optional});
I added this line after the OnInit of the template root.master.cs, this working fine on the first load but for the next loads there is this error :
A road named 'customapi' is already in the road collection. Route
names must be unique.
There is an application_start on Kentico where can I add this line?

As the OnInit event of Root.master.cs fires after each load of the page, you would essentially be mapping your route every time the page loads.
You need to be mapping the route on load of the application, not the page.
Create a custom module, and set up your routes on init of the module. Modules are initialised when the application starts.
using System.Web.Http;
using CMS;
using CMS.DataEngine;
[assembly: RegisterModule(typeof(MyCustomModule))]
public class MyCustomModule : Module
{
public MyCustomModule() : base("MyCustomModule") { }
// Called when the application starts
protected override void OnInit()
{
base.OnInit();
GlobalConfiguration.Configuration.Routes.MapHttpRoute("customapi", "customapi/{controller}/{id}", new { id = System.Web.Http.RouteParameter.Optional });
}
}

Related

What information can be returned to the DotNetNuke ModuleSearchBase via an API Controller?

We currently have the following code on our custom DNN module:
public class FeatureController : ModuleSearchBase
{
public CommonDataDefinitions.Products.WebProductDetails ProductDetails { get; set; } = null;
public override IList<SearchDocument> GetModifiedSearchDocuments(ModuleInfo moduleInfo, DateTime beginDateUtc)
{
var searchDocuments = new List<SearchDocument>
{
WHAT CAN I RETURN HERE?
};
return searchDocuments;
throw new NotImplementedException();
}
}
Our Detailed Product View module retrieves the following information depending on the SKU in a query string on load using a web API Controller.
Product.Title
Product.Description
Product.Image
Product.Price
Product.DetailedDescription
Product.StockCode Product.MetaTitle
Product.MetaKeywords
Product.MetaDescription
The SearchModulebase code will be in the FeatureController class.
This page will be loaded each time someone looks at a product in detail when they navigate from the Product Filter Module.
1. Since the module will be loaded each time when someone clicks on a particular product. How do you run this code only once and return all the products from the API Controller? Do we need to create an Object which will retrieve everything?
2. How do you prevent the module from becoming slow when all the products have to be retrieved on the on load event?
3. Which SearchDocument information can be returned for the DNN Crawler to index?
4. When the DNN Crawler reads the Feature Controller code, how do you initialize your API Controller to go and fetch and Populate the results to be indexed?

While loading the page, processRequest() won't called

I am new in OAF. I am writing a small program for displaying data in the browser window. But as per the OAF Documentation, when a page loads processRequest() should be called automatically. But in my case the processRequest() method is not called. So any one please help me to get the processRequest() method to be called when page is loaded.
This is my Controller code. Note I associate this controller to a page. While loading the page, processRequest() method is not called.
public class MyController extends OAControllerImpl
{
public static final String RCS_ID = "$Header$";
public static final boolean RCS_ID_RECORDED =
VersionInfo.recordClassVersion(RCS_ID, "%packagename%");
/**
* Layout and page setup logic for a region.
* #param pageContext the current OA page context
* #param webBean the web bean corresponding to the region
*/
public void processRequest(OAPageContext pageContext, OAWebBean webBean)
{
/* The below code line is used to initialize the application module */
System.out.println("inside processRequest");
OAApplicationModule am =
(OAApplicationModule)pageContext.getApplicationModule(webBean);
// am.invokeMethod("execVO");
/* The below code line is used to initialize VO*/
OAViewObject vo = (OAViewObject)am.findViewObject("EmpView1");
/* DataDisplayVO1 is the instance name in AM which is the original name of the VO */
vo.executeQuery();
RowSetIterator rowsetIterator = vo.createRowSetIterator(null);
while (rowsetIterator.hasNext())
{
Row r = rowsetIterator.next();
System.out.println("Empno is ... " + r.getAttribute("Empno"));
}
}
This is impossible. In my experience, till now I have never faced any issues like this.
On a second thought, just thinking if the controller is not assigned to the page. It may happen although in rarest case. And the case is you have run the page before attaching the controller and the page xml is stored in your classes directory. This directory is refreshed on each run, but rarely it doesn't get refreshed.
Try to rebuild your application, if possible delete the classes folder content of your relevant package. Hopefully it may help.

How to call method from any other page without declaring method as static using GEB and Spock?

I want to use following function in 2-3 pages so i want to call that function but getting exception "static method is required..."
Here in Code CommonFunction is a class which extends Page Class
def "User clicks on My Account Link"(){
CommonFunction."User clicks on My Account Link"()
}
I tried following approach also but still getting exception "geb.error.PageInstanceNotInitializedException: Instance of page class com.casestudy.util.CommonFunctions has not been initialized. Please pass it to Browser.to(), Browser.via(), Browser.page() or Browser.at() before using it."
CommonFunctions commonfunction= new CommonFunctions()
def "User clicks on My Account Link"(){
commonfunction."User clicks on My Account Link"()
}
here is the code for the function User clicks on My Account link()
def "User clicks on My Account Link"(){
def actions = new Actions(driver)
myaccountMenu.displayed
WebElement myaccountMenu = myaccountMenu.firstElement()
actions.click(myaccountMenu).build().perform()
}
It's a bit unclear what you're trying to do exactly, but it looks like you have a "My Account" link on several pages, and you want to have a way to click this without repeating code on every page. If that's the case you should do this:
Create a "BasePage" class which all your other pages that have your "My Account" functionality should extend.
class BasePage extends Page{
static content = {
myAccountLink { $("#myAccountLink") } //CSS selector for the link you want on your page
}
//You don't actually need this method. In your tests you can just say myAccountLink.click() and it'll still work.
def "User clicks on My Account Link"(){
myAccountLink.click()
}
}
Then your other pages can be defined as:
class HomePage extends BasePage {
...
}
And in your tests whenever the browser is at a Page which extends from BasePage, you'll be able to call the method:
def "User can go to my account page"(){
setup:
to HomePage
when: "The user clicks the MyAccount link"
"User clicks on My Account Link"()
then:
at MyAccountPage
}
I have changed function name inside the child class and then calling the function inside child function (here parent function name is now different from child function name)
Thank you Jk what you suggested that is also working fine
If you have a common component that can exist on multiple pages, then what you want to do is to create a Geb 'Module' with its control accessors and then include that module in the pages that contain that module.
class moduleName extends Module {
static clickUserLink() { userLink.click() }
static content = {
userLink = {$(locator)}
}
}
class pageName extends Page {
static content = {
localName { module moduleName }
}
}
That way your clickUserLink migrates to any page you've imported the module via localName.clickUserLink. You might also consider adding a waitFor(userLink) in the clickUserLink method or helper method.

Orchard CMS: Do I have to add a new layer for each page when the specific content for each page is spread in different columns?

Lets say I want a different main image for each page, situated above the page title. Also, I need to place page specific images in the left bar, and page specific text in the right bar. In the right and left bars, I also want layer specific content.
I can't see how I can achieve this without creating a layer for each and every page in the site, but then I end up with a glut of layers that only serve one page which seems too complex.
What am I missing?
If there is a way of doing this using Content parts, it would be great if you can point me at tutorials, blogs, videos to help get my head round the issue.
NOTE:
Sitefinity does this sort of thing well, but I find Orchard much simpler for creating module, as well as the fact that it is MVC which I find much easier.
Orchard is free, I understand (and appreciate) that. Just hoping that as the product evolves this kind of thing will be easier?
In other words, I'm hoping for the best of all worlds...
There is a feature in the works for 1.5 to make that easier, but in the meantime, you can already get this to work quite easily with just a little bit of code. You should first add the fields that you need to your content type. Then, you are going to send them to top-level layout zones using placement. Out of the box, placement only targets local content zones, but this is what we can work around with a bit of code by Pete Hurst, a.k.a. randompete. Here's the code:
ZoneProxyBehavior.cs:
=====================
using System;
using System.Collections.Generic;
using System.Linq;
using ClaySharp;
using ClaySharp.Behaviors;
using Orchard.Environment.Extensions;
namespace Downplay.Origami.ZoneProxy.Shapes {
[OrchardFeature("Downplay.Origami.ZoneProxy")]
public class ZoneProxyBehavior : ClayBehavior {
public IDictionary<string, Func<dynamic>> Proxies { get; set; }
public ZoneProxyBehavior(IDictionary<string, Func<dynamic>> proxies) {
Proxies = proxies;
}
public override object GetMember(Func<object> proceed, object self, string name) {
if (name == "Zones") {
return ClayActivator.CreateInstance(new IClayBehavior[] {
new InterfaceProxyBehavior(),
new ZonesProxyBehavior(()=>proceed(), Proxies, self)
});
}
// Otherwise proceed to other behaviours, including the original ZoneHoldingBehavior
return proceed();
}
public class ZonesProxyBehavior : ClayBehavior {
private readonly Func<dynamic> _zonesActivator;
private readonly IDictionary<string, Func<dynamic>> _proxies;
private object _parent;
public ZonesProxyBehavior(Func<dynamic> zonesActivator, IDictionary<string, Func<dynamic>> proxies, object self) {
_zonesActivator = zonesActivator;
_proxies = proxies;
_parent = self;
}
public override object GetIndex(Func<object> proceed, object self, IEnumerable<object> keys) {
var keyList = keys.ToList();
var count = keyList.Count();
if (count == 1) {
// Here's the new bit
var key = System.Convert.ToString(keyList.Single());
// Check for the proxy symbol
if (key.Contains("#")) {
// Find the proxy!
var split = key.Split('#');
// Access the proxy shape
return _proxies[split[0]]()
// Find the right zone on it
.Zones[split[1]];
}
// Otherwise, defer to the ZonesBehavior activator, which we made available
// This will always return a ZoneOnDemandBehavior for the local shape
return _zonesActivator()[key];
}
return proceed();
}
public override object GetMember(Func<object> proceed, object self, string name) {
// This is rarely called (shape.Zones.ZoneName - normally you'd just use shape.ZoneName)
// But we can handle it easily also by deference to the ZonesBehavior activator
return _zonesActivator()[name];
}
}
}
}
And:
ZoneShapes.cs:
==============
using System;
using System.Collections.Generic;
using Orchard.DisplayManagement.Descriptors;
using Orchard;
using Orchard.Environment.Extensions;
namespace Downplay.Origami.ZoneProxy.Shapes {
[OrchardFeature("Downplay.Origami.ZoneProxy")]
public class ZoneShapes : IShapeTableProvider {
private readonly IWorkContextAccessor _workContextAccessor;
public ZoneShapes(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
}
public void Discover(ShapeTableBuilder builder) {
builder.Describe("Content")
.OnCreating(creating => creating.Behaviors.Add(
new ZoneProxyBehavior(
new Dictionary<string, Func<dynamic>> { { "Layout", () => _workContextAccessor.GetContext().Layout } })));
}
}
}
With this, you will be able to address top-level layout zones using Layout# in front of the zone name you want to address, for example Layout#BeforeContent:1.
ADDENDUM:
I have used Bertrand Le Roy's code (make that Pete Hurst's code) and created a module with it, then added 3 content parts that are all copies of the bodypart in Core/Common.
In the same module I have created a ContentType and added my three custom ContentParts to it, plus autoroute and bodypart and tags, etc, everything to make it just like the Orchard Pages ContentType, only with more Parts, each with their own shape.
I have called my ContentType a View.
So you can now create pages for your site using Views. You then use the ZoneProxy to shunt the custom ContentPart shapes (Parts_MainImage, Parts_RightContent, Parts_LeftContent) into whatever Zones I need them in. And job done.
Not quite Sitefinity, but as Bill would say, Good enough.
The reason you have to create your own ContentParts that copy BodyPart instead of just using a TextField, is that all TextFields have the same Shape, so if you use ZoneProxy to place them, they all end up in the same Zone. Ie, you build the custom ContentParts JUST so that you get the Shapes. Cos it is the shapes that you place with the ZoneProxy code.
Once I have tested this, I will upload it as a module onto the Orchard Gallery. It will be called Wingspan.Views.
I am away on holiday until 12th June 2012, so don't expect it before the end of the month.
But essentially, with Pete Hurst's code, that is how I have solved my problem.
EDIT:
I could have got the same results by just creating the three content parts (LeftContent, RightContent, MainImage, etc), or whatever content parts are needed, and then adding them to the Page content type.
That way, you only add what is needed.
However, there is some advantage in having a standard ContentType that can be just used out of the box.
Using placement (Placement.info file) you could use the MainImage content part for a footer, for example. Ie, the names should probably be part 1, part 2, etc.
None of this would be necessary if there was a way of giving the shape produced by the TextField a custom name. That way, you could add as may TextFields as you liked, and then place them using the ZoneProxy code. I'm not sure if this would be possible.

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