Need experts advise: rewrite source code or alway develop a module (best practice?)?

Topics: Core, Customizing Orchard, General, Writing modules
Jan 25, 2013 at 11:46 PM

Hi,

We have a request to use Orchard, but need to replace the existing User Account actions.

Still use Form Authentication, but need to change the business logic in Register and Login actions.

In this case, shall we Re-write the source code of: \src\Modules\Orchard.Users\ 

OR, develop a custom module? (is it the best practice?)

If we will need to develop a custom module, how to override the existing user actions (Register/Login)?

 

Thanks!

Developer
Jan 26, 2013 at 12:50 PM

I did this at one on the companies I put Orchard in to. What I did was to create a separate module, copy the logic over and use OrchardSuppressDependency to stop it from using the other class...

I would always be hesitant in modifying the core code, doing that will make upgrading to new versions of Orchard much trickier.

Nick

Jan 26, 2013 at 1:02 PM

Thanks Nick,

Could you be more specific about your approach?

Yes, I just need to override the Login and Register action, what should I do with a empty custom module?

Sorry I'm new to Orchard.

Jan 26, 2013 at 9:22 PM

When I did this I just created a separate module, with a controller that rendered my own login and register views, and contained my custom logic. Then you just copy the stuff from the core controllers that log users into the system, eg IAuthenticationService and call authenticationService.SignIn(user, true). Shouldn't be to bad, a lot of the Orchardy stuff you can probably just copy out of the core. 

Jan 27, 2013 at 11:51 AM

Hi,

Is this what you mean?

1. Create and register a new module call MyModule;

2. Create a controller call: AccountModule.cs, and add following code (copied from src\Modules\Orchard.Users\Controllers\AccountController.cs)

3. Note it has been modified by adding a OrchardSuppressDependency.

However it doesn't work, the LogOn class hasn't been triggered at all (when debugging it).

 

namespace MyModule.Controllers
{
    [OrchardSuppressDependency("Orchard.Users.Controllers.AccountController")]
    public class AccountController : Controller
    {
        private readonly IAuthenticationService _authenticationService;
        private readonly IMembershipService _membershipService;
        private readonly IUserService _userService;
        private readonly IOrchardServices _orchardServices;
        private readonly IEnumerable<IUserEventHandler> _userEventHandlers;

        public AccountController(
            IAuthenticationService authenticationService,
            IMembershipService membershipService,
            IUserService userService,
            IOrchardServices orchardServices,
            IEnumerable<IUserEventHandler> userEventHandlers)
        {
            _authenticationService = authenticationService;
            _membershipService = membershipService;
            _userService = userService;
            _orchardServices = orchardServices;
            _userEventHandlers = userEventHandlers;
            Logger = NullLogger.Instance;
            T = NullLocalizer.Instance;
        }

        public ILogger Logger { get; set; }
        public Localizer T { get; set; }

        [AlwaysAccessible]
        public ActionResult LogOn()
        {
            if (_authenticationService.GetAuthenticatedUser() != null)
                return Redirect("~/");

            var shape = _orchardServices.New.LogOn().Title(T("Log On").Text);
            return new ShapeResult(this, shape);
        }

        [HttpPost]
        [AlwaysAccessible]
        [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
            Justification = "Needs to take same parameter type as Controller.Redirect()")]
        public ActionResult LogOn(string userNameOrEmail, string password, string returnUrl, bool rememberMe = false)
        {
            var user = ValidateLogOn(userNameOrEmail, password);
            if (!ModelState.IsValid)
            {
                var shape = _orchardServices.New.LogOn().Title(T("Log On").Text);
                return new ShapeResult(this, shape);
            }

            _authenticationService.SignIn(user, rememberMe);
            foreach (var userEventHandler in _userEventHandlers)
            {
                userEventHandler.LoggedIn(user);
            }

            return this.RedirectLocal(returnUrl);
        }

        private IUser ValidateLogOn(string userNameOrEmail, string password)
        {
            bool validate = true;

            if (String.IsNullOrEmpty(userNameOrEmail))
            {
                ModelState.AddModelError("userNameOrEmail", T("You must specify a username or e-mail."));
                validate = false;
            }
            if (String.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", T("You must specify a password."));
                validate = false;
            }

            if (!validate)
                return null;

            var user = _membershipService.ValidateUser(userNameOrEmail, password);
            if (user == null)
            {
                ModelState.AddModelError("_FORM", T("The username or e-mail or password provided is incorrect."));
            }

            return user;
        }

    }
}
Jan 27, 2013 at 1:20 PM

OK, after "Enable" my module, once click the "Sign In" link on the portal homepage, I got this error in VS:

The controller for path '/Orchard.Web/Users/Account/LogOn' was not found or does not implement IController.

Developer
Jan 27, 2013 at 5:18 PM

You will probably have to suppress the Orchard Users Routes file and point the new stuff at yours.

Jan 27, 2013 at 10:53 PM

Suppress the Orchard Users Routes file?

Could you be more specific, as there isn't any documentation for that.

Thanks!

Feb 13, 2013 at 12:15 AM
Add this class inside your module to override Orchard.User default routes to your own controller
public class UserRoutes : IRouteProvider
    {
        public UserRoutes()
        {
        }

        public void GetRoutes(ICollection<RouteDescriptor> routes)
        {
            foreach (var routeDescriptor in GetRoutes())
                routes.Add(routeDescriptor);
        }

        public IEnumerable<RouteDescriptor> GetRoutes()
        {
            return new[] {
                             new RouteDescriptor {
                                                    Priority = 9,
                                                    Route = new Route(
                                                        "Users/Account/Register",
                                                        new RouteValueDictionary {
                                                                                    {"area", "MyModule"},
                                                                                    {"controller", "Account"},
                                                                                    {"action", "Register"}
                                                                                },
                                                        new RouteValueDictionary(),
                                                        new RouteValueDictionary {
                                                                                    {"area", "MyModule"}
                                                                                },
                                                        new MvcRouteHandler())
                                                    },
                            new RouteDescriptor {
                                                    Priority = 9,
                                                    Route = new Route(
                                                        "Users/Account/LogOn",
                                                        new RouteValueDictionary {
                                                                                    {"area", "MyModule"},
                                                                                    {"controller", "Account"},
                                                                                    {"action", "LogOn"}
                                                                                },
                                                        new RouteValueDictionary(),
                                                        new RouteValueDictionary {
                                                                                    {"area", "MyModule"}
                                                                                },
                                                        new MvcRouteHandler())
                                                    }
            };

        }
    }
Developer
Feb 13, 2013 at 2:27 PM
Exactly. Overriding controllers is a bit trickier as existing routes specify the area which points the framework to look for a controller in a given module. If you suppress existing controller the existing route is no longer valid, so you need to specify a new one.

Actually suppressing a controller is not necessary - just specify the same route in your module, but pointing to your controller/area instead.