Custom Home Page Provider for Static Pages in Orchard

July 27th, 2010

Orchard is built with a home page provider pattern so that you can say this content item is the home page. The problem is if your homepage is something that doesn’t exist as a content item.. i.e. a static page.

The problem is that there is no way to tell orchard that a custom static page is the home page, so you need to buy in to the provider model, and the ‘trick’ it. So here is how I have done it.

public class CustomHomePageProvider : IHomePageProvider {
    public CustomHomePageProvider() {
        CurrentSite.HomePage = "CustomHomePageProvider;0";
    }

    protected virtual ISite CurrentSite { get; [UsedImplicitly] private set; }

    public string GetProviderName() {
        return "CustomHomePageProvider";
    }

    public ActionResult GetHomePage(int itemId) {
        //  We ignore the itemId as this is the part that we dont want.
        return new ViewResult {
            ViewName = "~/Modules/Orchard.LathamImages/Views/Test/Index.ascx",
            ViewData = new ViewDataDictionary<IndexViewModel>(new IndexViewModel())
        };
    }
}

First you need to create a class that implements the IHomePageProvider.

You can then make the assignment of the home page provider by always reassigning it in your ctor. (Yes I know I’m calling a virtual method in a ctor)

Next, set the view name in the GetHomePage method passing in any know ViewModel etc.

That’s It… now every time you start your site up, you will be using this home page.

The problem is that you can now never change the homepage… Well, food for thought I guess..

Orchard ,

Custom Redirect Results in Asp.Net MVC

July 27th, 2010

So after writing lots of code with return new redirect(“”) or RedirectToRoute etc, I decided i had had enough, and thought it would be a little nicer to have something return that made sense and could be reused, instead of stings for locations all over the place making my code look nasty.

So here is the the problem code that has lots of things that look like this…

return Redirect("/paymentoffline");

So instead of having this code smell, why not replace it with a class, somewhere we only have to specify the url once?

public class PaymentOfflineResult : RedirectResult {
    public PaymentOfflineResult()
        : base("/paymentoffline") {
    }
}

And now all we have to do is….

return new PaymentOfflineResult();

Is that not a lot cleaner??

MVC Hosting ,

AttachUpdated command for Entity Framework

July 27th, 2010

So who here has tried to get an entity that is disconnected reattached with all relevant references updated? I know i have… And after scouting the web for solution after solution here are the set of methods that works for me… They are a mismatch better a lot of other people methods, but this seems to be the best combination and so far I have had no problems.

Note : That my entitykeys are usually ‘Id’ so if your are different, then you will need to slightly change the code i.e. where is says ‘Id’

public static class EntityFrameworkExtensionMethods {

    public static void AttachUpdated(this ObjectContext ctx, EntityObject objectDetached) {
        if (objectDetached.EntityKey == null) {
            String entitySetName = GetEntitySetFullName(ctx, objectDetached);
            int objectId = (int)objectDetached.GetType().GetProperty("Id").GetValue(objectDetached, null);
            objectDetached.EntityKey = new System.Data.EntityKey(entitySetName, "Id", objectId);
        }
        if (objectDetached.EntityState == EntityState.Detached || objectDetached.EntityState == EntityState.Modified) {
            object currentEntityInDb = null;
            if (ctx.TryGetObjectByKey(objectDetached.EntityKey, out currentEntityInDb)) {
                ctx.ApplyPropertyChanges(objectDetached.EntityKey.EntitySetName, objectDetached);
                ctx.ApplyReferencePropertyChanges((IEntityWithRelationships)objectDetached,
                                                  (IEntityWithRelationships)currentEntityInDb);  //extension
            }
            else {
                throw new ObjectNotFoundException();
            }
        }
    }

    public static string GetEntitySetFullName(this ObjectContext context, EntityObject entity) {
        // If the EntityKey exists, simply get the Entity Set name from the key
        if (entity.EntityKey != null) {
            return entity.EntityKey.EntitySetName;
        }
        else {
            string entityTypeName = entity.GetType().Name;
            var container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
            string entitySetName = (from meta in container.BaseEntitySets
                                    where meta.ElementType.Name == entityTypeName
                                    select meta.Name).First();

            return container.Name + "." + entitySetName;
        }
    }

    public static void ApplyReferencePropertyChanges(this ObjectContext ctx, IEntityWithRelationships newEntity, IEntityWithRelationships oldEntity) {
        foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds()) {
            var oldRef = relatedEnd as EntityReference;
            if (oldRef != null) {
                var newRef =
                    newEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName) as
                    EntityReference;

                if (newRef != null && newRef.EntityKey == null) {
                    PropertyInfo info = newRef.GetType().GetProperty("Value",
                                                                     BindingFlags.Public | BindingFlags.Instance);
                    object val = info.GetValue(newRef, null);
                    if (val != null) {
                        string entitySetName = string.Empty;
                        var objVal = val as EntityObject;
                        if (objVal != null)
                            entitySetName = GetEntitySetFullName(ctx, objVal);

                        newRef.EntityKey = ctx.CreateEntityKey(entitySetName, val);
                    }
                    else if (oldRef.EntityKey != null) {
                        oldRef.EntityKey = null;
                    }
                }

                oldRef.EntityKey = newRef.EntityKey;
            }
        }
    }
}

You can use this by doing so… Where Student is your EntityObject

public int Save(Student student) {
    using (var context = new sitsEntities()) {
        if ((student.Code != null) && (student.EntityState == EntityState.Detached
            || student.EntityState == EntityState.Modified))
            context.AttachUpdated(student);
        else if (student.Code == null) {
            context.AddObject(context.GetEntitySetFullName(student), student);
        }

        return context.SaveChanges(true);
    }
}

 

Any issues let me know, as I use this code in live environments!!!

Entity Framework ,

Making menus in Orchard a little better

July 23rd, 2010

After keeping an eye on the Orchard forums and then doing some work on a clients website that has been implemented in Orchard, I soon became one of those people needing a decent menu. So, I thought to myself, well what does orchard have that I can use, after all – I don’t really want to have to do a lot of work just for a menu.

Note : This is all based around the last iteration of Orchard within Visual Studio 2008 framework 3.5.

So what does Orchard have that I can use…

1. Admin page for Menu
2. Allows you to store menu positions as 1 1.1 1.1.1 2 2.2 etc…
3. You can override the menu within your theme

Okay, so I got to thinking, if I can store the positions broken down like this, why cant i retrieve them like this, and go on to build the necessary menu steps? Well I can… here is how..

Step 1: In your theme, create a Menu.ascx file.

And drop this code in to it. (this is a modified version of the one located in Orchard.Themes)

<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Mvc.ViewModels.BaseViewModel>" %>
<%@ Import Namespace="Orchard.Utility.Extensions"%><%
    var menu = Model.Menu.FirstOrDefault();
    if (menu != null && menu.Items.Count() > 0) { %>
    <ul class="menu" role="navigation"><%
        int counter = 0, count = menu.Items.Count() - 1;
        int previousLevel = 1;
        int currentLevel = 1;
        foreach (var menuItem in menu.Items.OrderBy(m => m.Position)) {
            var sbClass = new StringBuilder(10);
            if (counter == 0)
                sbClass.Append("first ");
            if (counter == count) {
                sbClass.Append("last ");
            }
            if (string.Equals(menuItem.Href, Request.ToUrlString(), StringComparison.InvariantCultureIgnoreCase))
                sbClass.Append("current ");

            currentLevel = menuItem.Position.Split('.').Length;
            if ((previousLevel == currentLevel) && counter >= 1){ %>
                </li>
            <%}
            if (previousLevel < currentLevel) {
                sbClass.Append("first ");%>
                <ul>
            <% }
            if (previousLevel > currentLevel) {
                for (int i = 0; i < (previousLevel - currentLevel); i++) { %>
                </li> </ul>
            <% } %> </li> <%}
            var classValue = sbClass.ToString().TrimEnd();
            var linkAttributes = new Dictionary<string, object>();
            if (!string.IsNullOrEmpty(menuItem.AccessKey))
                linkAttributes.Add("accesskey", menuItem.AccessKey);
            %>
                <li<%=!string.IsNullOrEmpty(classValue) ? string.Format(" class=\"{0}\"", classValue) : "" %>><%=Html.Link(menuItem.Text, menuItem.Href, linkAttributes)%>

            <% previousLevel = currentLevel;
               if (counter == count) { %>
                </li>
            <% for (int i = 1; i < previousLevel; i++) { %>
                </ul> </li>
            <% } }

            ++counter;
        } %>
    </ul>
<% } %>

Next head to your manage menu section in the admin screens…. usually http://localhost:30320/Admin/Navigation

Set up your positions like below… Click Update All

image

 

Next head back to your front page, and you should start seeing html like so….

<div class="menucontainer">
    <ul class="menu" id="nav-one">
        <li class="first"><a href="/">Home</a></li>
        <li><a href="/Test/Gallery">Portfolio</a>
            <ul class="nested">
                <li class="last"><a href="/Test/Gallery/Item/University Work">University Work</a></li>
            </ul>
        </li>
    </ul>
</div>

 

 

I admit, I have done this in a little bit of a rush, so there could be one or two bugs creeping around.

Okay once this is done… So can then apply Suckerfish or what ever over the top… i used http://be.twixt.us/jquery/suckerFish.php which is the jquery equivalent.

Happy Orchard Picking everyone.

 

Note : Fixed issue with it not showing the ‘First’ class correctly. 29/July/2010

Note : Fixed issue with missing closing </li>. 04/Aug/2010

Nick

Orchard ,

Twitter Tweet Tweet Just Joined

February 23rd, 2010

Hey all… I have just joined Twitter!!.. if you wish to follow…

http://twitter.com/NicholasMayne

See you there! :)

Uncategorized

Orchard CMS – Simple Profile Package

February 6th, 2010

I started to do some work on profiles for a site we were building at work, and then we decided to jump ship on to Orchard. While Profiles haven’t really been asked for as a requirement at work, i noticed the Orchard team have it on there back log, and as i wanted it and I’m a developer, why wait… why not just write one!.

This solution have is very basic Profiling -

  • Forename, Middle name, Surname
  • Date Of Birth
  • Age – Derived from DOB
  • Gender
  • Occupation
  • Image Name – There is a setting to tell the url builder where to get your image from.
  • Website address
  • IsPrivate mode
  • Edit and View Mode + Anom viewing
  • Simple Profile Permissions attached to roles.

To install it

  1. Unzip The Orchard.Profiles Module to under the \Orchard.Web\Modules\Orchard.Profiles
  2. Open the Orchard.sln solution
  3. Right Click on Modules and Add existing Items Browse to and select \Orchard.Web\Modules\Orchard.Profiles\Orchard.Profile.csproj and Add
  4. Right click on the Orchard.Web solutions References and Add a project reference to Orchard.Profiles to which you have just added.
  5. Hit F5 And it should be plugged in, compiled and ready for use.
    Once you Register a user, you should be able to then go to http://localhost:30320/Profile/<UserName>
    If you have any problems getting it working leave a comment and i would be happy to help.

Here is a couple screenshots of it working…

Viewing a Profile.. Note the Url in the address bar.

image

The Edit button that appears on the right, will only appear if you have permission to edit your profile. This can be set in the Permissions section in Orchard.

If the profile has be set to private, then the page will redirect to a ProfilePrivateResult ONLY if you are attempting to view a profile other than your own.

Editing a Profile is a little different, as i haven’t plugged in all the pretty moving things.

image

Just so you are aware, the Roles section down the bottom is only shown if a user can modify their roles, this is coming from Orchard.Roles.

The Image Name will be the image i.e. AnImage.png, A Url helper will then fully qualify this based on a setting called Image Folder, which can be modified in the Orchard Settings Section.

image

Note the setting down the bottom.

image

So if you add an image to Media/Profiles/Images

image

Then you add that name to the ImageName field on the Profile Edit page then you should see something that looks like this…

Hit Save… And Hey Presto!! You have an image… granted, mine is a bit squashed!

image

Anyways here is the code for download.

Download Code (C#) -

This will be an ongoing piece of work, but i will update this page, and add pages for more advanced features in the future.

Let me know how you get on, and please feed back any Constructive criticism you have, as it will only make my work better for me to upload to you lot.

This code was written in C# against Orchard Change set d95b79b01966.

Orchard , , , , ,

Off Travelling – Take 2

January 28th, 2010

Well i have handed my notice in at work to embark on a new travelling experience with Penny… Here is the planned itinerary…

1. Fly to Boston… Spend a couple nights there

2. Next onwards to Maine… I really want to give Ice Diving a go.. Sounds Cold huh.

3. Pen wants to go to Niagara Falls, so i think we will go there

4. After that Me and Pen are headed to my cousins Wedding Reception in Newtown, PA which should be awesome, and cheap!! – Hopefully Kathy will do my laundry

5. A couple days after that we fly to Lima in Peru and embark on the Inca Trail

6. I would then like to do a Jungle Lodge overlooking the Amazon rainforest.

7. After travelling around Peru for a while, and hitting one or two beaches, Pens idea! We are heading up to Ecuador

8. Scuba Scuba around the Galapagos islands here i come!

9. Fly home and look for a place to live and a job! – Microsoft Hire Me!

10. The End! – Photos on my facebook! http://www.facebook.com/nick.mayne?ref=name

Travelling

Complete Regular Expression for URL’s

January 28th, 2010

Today i was looking for a IsUrl(string) method to verify if a string is a URL or not and stumbled across this regex…. http://internet.ls-la.net/folklore/url-regexpr.html – make sure you have your hard hats on for this one!… Its the complete regex for a URL.

Nick Out!

Regex, URL

Hosting and Deployment of an ASP.NET MVC Application in IIS6 and IIS7

January 27th, 2010

Today I spent about 4 hours attempting to get an MVC2 application working under IIS6 on a windows server 2003 machine, only to end up with 404 errors where ever I turned – until a co-worker finally had a brain wave and said that i had to do something special in IIS to make sure routing works.

After stumbling across this post http://mvchosting.asphostcentral.com/post/ASPHostCentralcom-Hosting-and-Deployment-of-an-ASPNET-MVC-Application-in-IIS6-and-IIS7.aspx i got my asp.net app working in under 5 minutes.

Hopes this helps someone.

Cheers, Nick

MVC Hosting , , ,

ObjectContextScope – Entity Framework v1

January 25th, 2010

A while ago i had written some code to cope with the ever annoying issue on entities existing in different scopes… The problem is that one scope would not know if it was wrapped up in another scope, this would stop any type of abstraction know to mankind.

Anyways to cut a long story short i decided to do something similar to the TransactionScope and create a ObjectContextScope. This would now allow me to use Ambient scopes so that only one object context was being used at any one time unless otherwise stated.

An example as shown below is that

1. scope1 will create the initial object context of dbEntities.

2. scope2 will use scope1 object context as the objectcontext is of the same type of dbEntities.

3. scope3 will create its own object context, which can only be used in scope3

4. scope4 is of a different objectcontext type, so a new objectcontext is created of type db2Entities.

5. scope 5 will use scope1 object context as scope 3 is only for scope3 and cannot be used inside any other scope.

using (ObjectContextScope scope1 = new ObjectContextScope())
{
    using (ObjectContextScope scope2 = new ObjectContextScope())
    {
        using (ObjectContextScope scope3 = new ObjectContextScope
		(ObjectContextScopeOption.RequiresNew))
        {
            using (ObjectContextScope scope4 = new ObjectContextScope
		(ObjectContextScopeOption.Required))
            {
            }

            using (ObjectContextScope scope5 = new ObjectContextScope
		(ObjectContextScopeOption.RequiresNew))
            {

            }
        }
    }
}

Anyways, give it a go. The attachment is below. Any problems email me at Jetski5822@hotmail.com or leave a comment and let me know how you get on.

Entity Framework , , ,