Piranha CMS on existing MVC 5 site - piranha-cms

I am new to Piranha CMS (and in any CMS in general). I have a question about integrating the system to an existing MVC 5 application. Here is what I have done so far:
I have followed the steps in the Getting Started section
I have created the database and set up the connection string
I have created the admin user and can log in to the manager interface
What I am having trouble is understanding how to bring my existing pages into the manager and how to expose them to the users for editing. I did look into the documentation, but could not find anything dedicated to this topic.
Any help is greatly appreciated.
Thank you.
Daniel D.

It's really simple getting data from Piranha CMS into your existing application.
1. Page types
First of all, take a look at the different page types you need to create (i.e the different kind of page structures you need) and create them, either from the manager interface or by code. You'll find the docs here:
http://piranhacms.org/docs/pages/page-types
http://piranhacms.org/docs/extend/page-post-types
2. Create your pages
Next up, just create the pages you need in the manager and add the content. If you want to prohibit the users to add pages, you can always remove the "Add" buttons later, either by injecting CSS into the manager or by customizing it.
3. Adding the data to your models
Here you have two options, either you let your model inherit from the PageModel, or your just add a new property with the CMS data to your existing model.
// With inheritance
public class MyModel : Piranha.Models.PageModel {
...
}
// With the CMS data in a property
public class MyModel {
public Piranha.Models.PageModel CMSData { get; set; }
}
4. Getting the data
Now populating the data is just a calling a method. You can either access it by id or by permalink. Let's for example access it by permalink and let's say we have a page with the permalink about-us. Just add the following to your controller:
// With inheritance
public class MyController : Controller {
public ActionResult MyAction() {
// Get the CMS-data
var model = Piranha.Models.PageModel.GetByPermalink<MyModel>("about-us");
// Fill the model with your custom data
...
}
}
// With the CMS data in a property
public class MyController : Controller {
public ActionResult MyAction() {
// Create and fill your custom model
var model = new MyModel();
...
// Get the CMS-data
var model.CMSData = Piranha.Models.PageModel.GetByPermalink("about-us");
}
}
5. Configuration
Note that if you want your application to control the routing your should configure Piranha CMS to run in passive mode. You can find the docs on configuration here:
http://piranhacms.org/docs/api-reference/configuration
I hope this helps you get started!
Regards
/Håkan

Related

Access to container of Simple Injector MVC views

In a Sitecore project I've integrated Simple Injector using this article
It uses sitecore pipelines and then uses a method in App_start
namespace BBC.App_Start
{
public class SimpleInjector : IPackage
{
public void RegisterServices(Container container)
{
GetContainer.RegisterServices(container);
container.Register(() => new SitecoreContext(), Lifestyle.Scoped);
container.Register(() => new Container(), Lifestyle.Singleton);
}
}
}
Simply I can inject container into controller constructor but can't have container in View files.
I tried to declare a static property in App-start and save container to it. but still I'm getting no registration type in Views
What is the best way to have container object in views?
As Stephen suggests in his comment, the literal answer to your question is "you shouldn't do that - because it's not really the way MVC and DI are supposed to work". The more detailed answer goes something like this:
The job of your view is to present data that it has been passed via the Model. Views should not really contain logic. Very simple stuff like "if flag is false, hide this block of mark-up" is ok, but the more complex code to work out what the value of the flag is shouldn't be in the view.
MVC tries to make our website code better by encouraging you to separate presentation (the View) from data (the Model) and logic (the Controller). This should make our code easier to work with - So if you have processing that needs doing, then it should really be happening when your controller method runs.
If your view requires some special data, best practice suggests it should work it out in the controller method and pass it to the view in the model. The code might look more like this:
public class MyModel
{
public string SpecialData { get; set; }
}
public class MyController : Controller
{
public ActionResult DoSomething()
{
// do whatever processing is needed
var somethingCalculate = resultFromYourOtherObject();
// do other stuff
var model = new MyModel() { SpecialData = somethingCalculated };
return View(model);
}
}
And then the View just needs to accept the MyModel class as its model, and render the SpecialData property - no logic required.
I think also it's considered a bad idea to have calls to fetch objects from your DI container spread about your codebase. For MVC apps, generally your DI container gets wired in to the process of creating a controller for a request when the app starts up. Rather than passing about a DI Container into your controllers, the DI framework extends the Controller-creation process, and the container isn't exposed outside of this. When the MVC runtime needs to create a controller, the controller-creation logic uses the DI framework to fetch objects for all the controller's dependencies.
Without more detail about what you actually want to achieve, it's difficult to say what the "right" approach to creating your object(s) here is, but the two most common patterns are probably:
1) Constructor injection: Your controller has a parameter which accepts the object required. The DI container creates this object for you at the point where it creates the controller, so your controller gets all its dependencies when it is created. Good for: scenarios where you know how to create the object at the beginning of the request.
public interface IMySpecialObject
{
string DoSomething();
}
public class MyController : Controller
{
private IMySpecialObject _specialObject;
public MyController(IMySpecialObject specialObject)
{
_specialObject = specialObject;
}
public ActionResult RenderAView()
{
// do some stuff
var data = _specialObject.DoSomething();
return View(data);
}
}
As long as IMySpecialObject and a concrete implementation for it are registered with your DI container when your app starts up, all is well.
2) Factory classes: Sometimes, however, the object in question might be optional, or it might require data that's not available at controller-creation time to create it. In that case, your DI framework could pass in a Factory object to your controller, and this is used to do the construction of the special object later.
public interface ISpecialFactory
{
ISpecialObject CreateSpecialObject(object data);
}
public class MyController : Controller
{
private IMySpecialFactory _specialFactory;
public MyController(IMySpecialFactory specialFactory)
{
_specialFactory = specialFactory;
}
public ActionResult RenderAView()
{
// do some stuff
if( requireSpecialObject )
{
var data = getSomeData();
var specialObject = _specialFactory.CreateSpecialObject(data);
var data = _specialObject.DoSomething();
return View(data);
}
return View("someOtherView");
}
}
But a good book on using DI may suggest other approaches that fit your specific problem better.

Using attribute routing on a controller is forcing me to manage all routes

I am just getting to grips with Asp.net Core and I'm trying to set up a basic site.
I want to build an admin panel that is under a subdirectory.
I have a simple controller which was scaffolded by the EF crud feature.
So it seems that from the examples I should just be able to add a [Route()] attribute to the controller and it will prefix everything. Something like this:
[Route("Admin/Subfolder/[controller]")]
public class EventsController : Controller
{
}
But when I do that I just get an error page saying "multiple actions matched" and it lists index, details, create, etc.
I can get it working if I then go through every method and put a [Route()] attribute on it but this doesn't seem to be in line with the documentation.
It feels like I should be able to just add a prefix to the controller route without having to take over management of every route within the controller. Case in point, the POSTS are not working now and I'm not sure what the format of the route attribute should be for them.
What am I doing wrong?
You are doing it correctly. Default route attribute can be applied at the controller level. “Placing a route attribute on the controller makes all actions in the controller use attribute routing.”
Can you post complete code of your controller? There must be something else going on in there. Make sure you use HttpPost/HttpGet attribute for actions with the same name, like so:
[Route("Admin/Subfolder/[controller]")]
public class EventsController : Controller
{
[HttpGet]
public IActionResult NewEvent()
{ }
[HttpPost]
public IActionResult NewEvent()
{ }
}
Good explanation on routing can be found here

VS 2013 Controller Scaffolding Fails for the ApplicationUser Model (Multiple object sets per type are not supported)

In a VS 2013 RTM, MVC 5 project with EF 6, I tried to scaffold a controller based on the ApplicationUser (default with individual accounts authentication). Both ApplicationUser and IdentityUser are mapped to a Users table.
The wizard opens the context file for editing and tries to add a new db set for ApplicationUser (ApplicationUsers) and then fails with this error:
Unable to retrieve metadata for ApplicationUser. Multiple object sets per type are not supported. The object sets ApplicationUsers and Users can both contain instances of type ApplicationUser
The solution does not have any reference to, or instance of ApplicationUsers.
Is this a known issue? Can the scaffolding be run using command line with options (from PMC)?
Note: scaffolding also adds an extra db set to the context class if I specify a model that references ApplicationUser (the app works if I remove it and fix references in the generate controller).
Wow. I'm really surprise that no one actually got to the root of this, and instead, are just recommending workarounds.
IdentityDbContext already contains a property:
`public virtual IDbSet<TUser> Users { get; set; }
When you subclass IdentityDbContext to create your own application-specific context, you must specify what class satisfies the TUser generic. The default is:
public ApplicationDbContext : IdentityDbContext<ApplicationUser>
Which then means that you functionally have a property already via inheritance in the form of:
public IDbSet<ApplicationUser> Users { get; set; }
If you then add another property to your application-specific context such as:
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
You now have the same entity tracked by two DbSets, and you get that error. The solution? Simply don't add your own DbSet for ApplicationUser. There's no need to rename or override anything.
Short-Version: Rename your ApplicationUser class to User.
I've been running into this problem for about a month with absolutely no luck...until now!
Initially, I thought it was a preview issue, but after persisting into the RTM along with the latest libraries, I became incredibly annoyed, since this problem persisted into Migrations too.
However, IdentityDbContext, according to the error message, seems to be creating two DbSets: ApplicationUsers and Users. We only want Users when looking at the source code:
public class IdentityDbContext<TUser> : DbContext where TUser : Microsoft.AspNet.Identity.EntityFramework.IdentityUser
{
...
public virtual IDbSet<TUser> Users { get; set; }
...
}
From this, we (and the scaffolding engine, and the migrations engine) should only see "Users", not "ApplicationUsers".
To rectify this situation, you will need to adjust your application class to account for this rather strange error. Simply rename your ApplicationUser class to User:
using Microsoft.AspNet.Identity.EntityFramework
...
public class ApplicationUser : IdentityUser
{
Your Stuff
}
To:
using Microsoft.AspNet.Identity.EntityFramework
...
public class User: IdentityUser
{
Your Stuff
}
Attempt to Scaffold again. If you receive another error along the lines of the class cannot be found, save your project, close VS2013, re-open VS2013, load the project, re-build the project, and finally attempt to scaffold. The IdentityDBContext should no longer be creating a dummy "ApplicationUsers" DBSet object causing both Entity Migrations and Scaffolding to issue these errors.
Hope this helps!
P.S. Any mapping done ought not to affect this problem, so you should be able to still map to the same table if you wish to.
EDIT:
If you receive further problems, undo the rename. I ran into some problems (more scaffolding and query errors), and after I went back to ApplicationUser, those problems disappeared and the problem above did not re-occur. Just a heads up.
Read the above problems en solutions.
My error text was:
Multiple object sets per type are not supported. The object sets
'ApplicationUsers' and 'Users' can both contain instances of type
'DataLayerIdentity.Models.ApplicationUser'
I suspect the error was created when I was playing around and scaffolded the model: ApplicationUser in a new controller.
Solved it by removing the below from : ApplicationDbContext.cs
public System.Data.Entity.DbSet<DataLayerIdentity.Models.ApplicationUser> ApplicationUsers
{
get;
set;
}
No Other changes where made to solve the problem. I hope this helps someone.
When you use scaffolding to generate control, vs will auto insert 1 line to your db context
public System.Data.Entity.DbSet<...API.Models.ApplicationUser> ApplicationUsers { get; set; }
Just delete that line, and in your controller. change
db.ApplicationUsers to db.Users
Here is the simplest solution. When you add/scaffold a view (list) based on ApplicationUser as the model, VS2013 ADDS the following to the IdentityModels.vb or .cs file.:
Public Property ApplicationUsers As System.Data.Entity.DbSet(Of ApplicationUser)
Just remove this property and the problem goes away.
If you are trying to create an ApplicationUsersController please follow these steps.
Delete this line from IdentityModels.cs
public System.Data.Entity.DbSet<Project.Models.ApplicationUser> ApplicationUsers { get; set; }
Build the project
control + shift + b
Generate the controller
Right click on the 'Controllers' folder.
Add > Controller
MVC Controller with views, using Entity Framework
Model Class: ApplicationUser
Add
Go back to IdentityModels.cs and delete this line AGAIN
public System.Data.Entity.DbSet<Project.Models.ApplicationUser> ApplicationUsers { get; set; }
Build the project
control + shift + b
Change the database calls to from ApplicationUsers to Users in ApplicationUsersController.cs
control + f to bring up 'Find and Replace'
Click 'Replace in files'
Find what: db.ApplicationUsers
Replace with: db.Users
Replace All
Press play and cross fingers :)
What you can also do:
Create an empty controller, and add the code for the DataContext yourself
protected ApplicationDbContext db { get; private set; }
public HomeController() : this(new ApplicationDbContext())
{
}
public HomeController(ApplicationDbContext db)
{
this.db = db;
}
Then create your methods like Index, Create and so on, and create the views with right-clicking and "Add view..." for each.
Scaffold your views with the list, create, whatever template that is appropriate and choose the ApplicationUser as an model.
Important: Delete the entry in "Data context class", or you will get a similar error again. But if you leave the "Data context class" empty, the scaffolding of the view will work out fine.
I fixed problem by removing DbSet from context and then changing references in controller from ApplicationUsers to Users. It worked - but now i see no point in scaffolding users.
To much things has to by maintained on top level and it just does not work right. Now i know that view models and repository are the way I want to go.

initialize simple membership in MVC 4

I have a problem with my MVC 4 application which used to work fine, but stopped for some reason, and I cannot find out why. I use simple memebrship provider and code first approach. This is my Index action method in the home controller
[Authorize]
public class HomeController : Controller
{
private IActivityRepository repo;
public HomeController(IActivityRepository activityRepository)
{
repo = activityRepository;
}
//Allow anonymous to allow to create database if there isn't one yet
[AllowAnonymous]
public ActionResult Index()
{
repo.InitializeDatabase(); //!!!!!!!!!!!!!!!!!!!!!
return RedirectToAction("ManageActivities");
}
The whole concept of mine is that if database doesn't exist it gets created in InitializeDatabase Method. Then user is redirected to ManageActivities action method which is decorated with [Authorize] attribute, what in effect takes user to login action method in AccountCotroller (out of the box in MVC4). This controller is decorated with [InitializeSimpleMembership], what fires InitializeSimpleMembershipAttribute filter.
This logic worked fine for me a while ago. Today I wanted to create a new database for testing purposes. When I create data context I call the base class with a custom name for the database like so:
public class ActivityLogContext : DbContext
{
public ActivityLogContext() : base("ActivitiesConnection")
{
}
So I've changed details for my connection string and run the application. Unfortunatelly, for some reason the code hits InitializeSimpleMemebership filter before running Index method from the home controller (even though its decorated with [AllowAnonymous]). In effect simple membership is initialized but database does not yet exist, what runs me into error.
My question is, why InitializeSimpleMemebership filter is getting released on application start if Index method doesn't require authorization?
I would eliminate the use of the InitializeSimpleMembership as discussed in this article. Move initialization to the Global.asax Application_Start method and do your initialization there also, so that it happens in the correct sequence.

MVC 4 How to process a url parameter on every page, base controller?

Looking for some guidance in designing my new MVC 4 app.
I would like to have a url parameter s=2011 on every page of the app to let me know what year of data I'm working with. Obviously, the user will have a way to change that parameter as needed.
I will need that parameter in every controller and wondering the best way to do this. I was thinking of creating a base controller that reads Request.QueryString and puts the year into a public property. However, considering all the extensability points in MVC, I'm wondering if there's a better way to do this?
This very much depends on the design of your app, but just to give you two alternatives
IActionFilter
If you are doing data context per request you can use a global IActionFilter to hook pre-action execution globally and apply a query filter to your data context behind the scenes.
Major down-side of this is that to test the controller you will need to have the full MVC pipeline setup so that the actionfilter gets applied properly.
Dependency Injection
Instead of using sub-classing (base controller as you say) you can use dependency injection . Keeping things more loose will allow you to pull the filter from query string, cookie, user setting in the database or whatever else - without your controller knowing where it comes from.
Here is some pseudo code how I would do it if I was using something like Entity Framework or Nhibernate (also I am sure applicable with other technologies as well)
public Car
{
public string Year { get; set; }
}
public class CarsDataContext : DbContext
{
private IQuerable<Cars> _cars = null;
private Func<Car, bool> _carsFilter = null;
public IQuerable<Car> Cars {
get {
if (_carsFitler != null)
return _cars.Where(_carsFitler);
return _cars;
}
set { _cars = value; }
}
public void ApplyCarsFilter(Func<Car, bool> predicate)
{
_carsFilter = predicate;
}
}
Assuming you have dependency injection setup already (NInject or whichever other framework) in you can configure how the context to be intialized
Bind<CarsDataContext>().ToMethod(() => {
string yearFilter = GetYearFilter(); // can be coming from anywhere
CarsDataContext dataContext = new CarsDataContext();
dataContext.Applyfilter(car => car.Year == yearFilter);
return dataContext;
}).InRequestScope();
Then my controller knows nothing about the data filtering and I can easily test it:
class MyController : Controller
{
public MyController(CarsDataContext dataContext)
{
}
...
}
However I would only do this is filtering the dataset was across many controllers and important part of my software. Otherwise it's pure over-engineering.