Posted by admin under General  sBlog.Net v3.0  Bootswatch  on Jan 11 2014

Hello,

I have published 3 themes using bootswatch to github! Check them out, If you are not famililar with bootswatch, they are a set of themes using the bootstrap styles as the base! You can check them out here

As a sidenote, while trying these new themes, make sure you have the latest binaries for sBlog.Net (just copying the theme files with v2.0 won't work). Also, if you notice that the styles are messed up, you may have to restart the application pool, since the cutsom view engine of sBlog.Net may still be using the old theme. I am thinking about a much better way to deal with this. My previous post discusses about the custom view engines of sBlog.Net. But, for now, have fun with the new themes!!!

PS: This blog currently uses the Amelia theme!

Posted by admin under General  sBlog.Net v3.0  on Dec 01 2013

In this post, I would like to discuss the most recent check-in for sBlog.Net. With the latest update you will be able to create theme specific partial views and thus you are not limited by the partial views provided by the blog engine. For example, you could create a partial view that override's the way a post pager for the blog is displayed [used for navigating between posts]. For this to happen, a Shared folder has to be created within the theme's folder and then a partial view that correspond to the name (in this case PostPager.cshtml) of the partial view being overridden. This is possible because of a few changes to the SetupViewEngines method in the Application_Start event. It's given below:

   private void SetupViewEngines()
   {
       var settings = InstanceFactory.CreateSettingsInstance();
       ViewEngines.Engines.Clear();
       ViewEngines.Engines.Add(new CustomRazorViewEngine(settings.BlogTheme));
   }

Note how an instance of CustomRazorViewEngine is passed to the ViewEngines.Engines.Add. CustomRazorViewEngine is a class that overrides RazorViewEngine. The main function of this class is to set the locations to be searched for finding a view, partial view or a master. Here is the complete listing:

    public class CustomRazorViewEngine : RazorViewEngine
    {
        public CustomRazorViewEngine(string blogTheme)
        {
            base.AreaViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
            base.AreaMasterLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
            base.AreaPartialViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
            base.ViewLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
            base.MasterLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
            base.PartialViewLocationFormats = GetPartialViewLocations(blogTheme);
            base.FileExtensions = new[] { "cshtml", "vbhtml" };
        }

        private static string[] GetPartialViewLocations(string blogTheme)
        {
            var locations = new List<string>();

            if (HasSharedPartialViews(blogTheme))
            {
                var csPath = "~/Themes/" + blogTheme + "/Shared/{0}.cshtml";
                var vbPath = "~/Themes/" + blogTheme + "/Shared/{0}.vbhtml";
                locations.Add(csPath);
                locations.Add(vbPath);
            }

            locations.Add("~/Views/{1}/{0}.cshtml");
            locations.Add("~/Views/{1}/{0}.vbhtml");
            locations.Add("~/Views/Shared/{0}.cshtml");
            locations.Add("~/Views/Shared/{0}.vbhtml");

            return locations.ToArray();
        }

        private static bool HasSharedPartialViews(string blogTheme)
        {
            if (string.IsNullOrEmpty(blogTheme))
                return false;

            var pathMapper = DependencyResolver.Current.GetService<IPathMapper>();
            var directoryRelativePath = string.Format("~/Themes/{0}/Shared", blogTheme);
            var directoryPath = pathMapper.MapPath(directoryRelativePath);
            return Directory.Exists(directoryPath);
        }
    }

The constructor takes care of setting the locations. The thing to note in the constructor is the call to GetPartialViewLocations. This method is used to "find" out if the current theme contains a Shared folder. If so, it's added as the first entry to the list. It has to be the first entry since that's the order used for searching by the MVC framework. Thus, using this approach, a partial view residing within the appropriate theme folder gets the priority over the one in the common shared views folder. Hope you enjoyed this short post! Happy coding!

Posted by admin under General  sBlog.Net  Customizations  sBlog.Net v2.0  on Dec 01 2013

I have published yet another theme based on bootstrap! You can get it here! A demo helps always right? Visit the blog, it now uses the theme I just published! If you need some help on installing / using this theme, refer to this post!

Tagged sblog.net 
Posted by admin under General  sBlog.Net  Customizations  sBlog.Net v2.0  on Sep 07 2013

Hello people! 

I have published a new theme using bootstrap. It was in my to-do list for a long time and I am happy I got to this atlast! You can download this here! If you want to look at a sample, you are already seeing it!

To use this theme, download the zip file. Right click on the zip file, choose "Extract Here" [WinRAR] (If you choose "Extract to Folder.." the following may not apply. Once you extract the file, you will see the following folder structure:

                GoBootstrapDark
                                |
                                |
                                ----- css
                               |
                               ------ js
                               |
                               ------ img
                               | 
                               ------ _Layout.cshtml
                               | 
                               ----- _LayoutPage.cshmtl

With Themes folder in your web application, the same structure should be recreated (as shown below).

                Themes
                |
                ---- GoBootstrapDark
                                |
                                |
                                ----- css
                               |
                               ------ js
                               |
                               ------ img
                               | 
                               ------ _Layout.cshtml
                               | 
                               ----- _LayoutPage.cshmtl

That's it! Now, if you logon to the admin section,  go to the settings page, you will now see the new theme in the dropdown, select, update the settings, that's it! You are all set to rock on!!!

Tagged sblog.net  theming 
Posted by admin under General  Customizations  sBlog.Net v2.0  on Nov 15 2013

In this post, I am going to discuss a method by which you could change how the urls appear when a post's title is clicked. By default the when the title of the post is clicked it appears as "http://sblogproject.net/2013/09/new-theme-published-using-bootstrap". What if you wanted this to appear as "http://sblogproject.net/new-theme-published-using-bootstrap.html"? Here are the steps to do this!

We all love test driven development right? So let me start with changing the unit tests for the post routes.

// Update RouteTests.cs

[TestMethod]
public void Can_Identify_Individual_Post()
{
    var context = new MockHttpContext(0, false, "~/some-post.html");
    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
 
    var routeData = routes.GetRouteData(context);
 
    Assert.IsNotNull(routeData);
    Assert.AreEqual("Home", routeData.Values["controller"]);
    Assert.AreEqual("ViewPost", routeData.Values["action"]);
    Assert.AreEqual("some-post", routeData.Values["url"]);            
}
 
[TestMethod]
public void Can_Identify_Individual_Post_With_Status()
{
    var context = new MockHttpContext(0, false, "~/some-post.html/comment-posted");
    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
 
    var routeData = routes.GetRouteData(context);
 
    Assert.IsNotNull(routeData);
    Assert.AreEqual("Home", routeData.Values["controller"]);
    Assert.AreEqual("ViewPost", routeData.Values["action"]);
    Assert.AreEqual("some-post", routeData.Values["url"]);
    Assert.AreEqual("comment-posted", routeData.Values["status"]);
}

// Change HomeControllerTests.cs

[TestMethod]
public void Can_Return_Posts_By_URL()
{
    var httpContext = GetHttpContext(false, 0);
    var postController = GetHomeControllerInstance(httpContext);
    postController.ControllerContext = new ControllerContext { HttpContext = httpContext };
    var result = (ViewResult)postController.ViewPost("a-test-url-1", "");
    var model = result.ViewData.Model as ViewPostOrPageModel;
    Assert.IsNotNull(model);
    var post = model.Post;
    Assert.IsNotNull(post);
    Assert.AreEqual("Post Title 1", post.PostTitle);
    Assert.AreEqual(2012, post.PostAddedDate.Year);
    Assert.AreEqual(4, post.PostAddedDate.Month);
}
 
[TestMethod]
public void Can_Return_Posts_By_URL_Private_Owner()
{
    var httpContext = GetHttpContext(true, 1);
    var postController = GetHomeControllerInstance(httpContext);
    postController.ControllerContext = new ControllerContext { HttpContext = httpContext };
    var result = (ViewResult)postController.ViewPost("a-test-url-14", "");
    var model = result.ViewData.Model as ViewPostOrPageModel;
    Assert.IsNotNull(model);
    var post = model.Post;
    Assert.IsNotNull(post);
    Assert.AreEqual("[Private] Post Title 14", post.PostTitle);
    Assert.AreEqual(2012, post.PostAddedDate.Year);
    Assert.AreEqual(4, post.PostAddedDate.Month);
}
 
[TestMethod]
public void Can_Return_Posts_By_URL_Private_Admin()
{
    var httpContext = GetHttpContext(true, 1);
    var postController = GetHomeControllerInstance(httpContext);
    postController.ControllerContext = new ControllerContext { HttpContext = httpContext };
    var result = (ViewResult)postController.ViewPost("a-test-url-11", "");
    var model = result.ViewData.Model as ViewPostOrPageModel;
    Assert.IsNotNull(model);
    var post = model.Post;
    Assert.IsNotNull(post);
    Assert.AreEqual("[Private] Post Title 11", post.PostTitle);
    Assert.AreEqual(2012, post.PostAddedDate.Year);
    Assert.AreEqual(4, post.PostAddedDate.Month);
}        
 
[TestMethod]
[ExpectedException(typeof(UrlNotFoundException), "Unable to find a post w/ the url a-test-url-25 for the month 01 and year 2012")]
public void Can_Throw_Appropriate_Exception_When_Accessing_Private_Post()
{
    var httpContext = GetHttpContext(true, 1);
    var postController = GetHomeControllerInstance(httpContext);
    postController.ControllerContext = new ControllerContext { HttpContext = httpContext };
    var result = (ViewResult)postController.ViewPost("a-test-url-25", "");
    var model = result.ViewData.Model as ViewPostOrPageModel;
    Assert.IsNotNull(model);
    var post = model.Post;
}
 
[TestMethod]
[ExpectedException(typeof(UrlNotFoundException), "Unable to find a post w/ the url a-test-url-10 for the month 04 and year 2012")]
public void Can_Return_Posts_By_URL_Private_Non_Admin()
{
    var httpContext = GetHttpContext(true, 2);
    var postController = GetHomeControllerInstance(httpContext);
    postController.ControllerContext = new ControllerContext { HttpContext = httpContext };
    var result = (ViewResult)postController.ViewPost("a-test-url-10", "");
    var model = result.ViewData.Model as ViewPostOrPageModel;
    Assert.IsNotNull(model);
    var post = model.Post;
}

Next, the route definition for an individual post has to be updated in order to modify the url format as shown below:

routes.MapRoute("IndividualPost", "{url}.html/{status}",
                new { controller = "Home", action = "ViewPost", status = UrlParameter.Optional },
                new { url = @"\S+", status = @"[a-z\-]*" });

If you notice, the value for action has been changed to "ViewPost" from "View". Now the controller has to be updated to take care of these changes. To begin with I have change the method name to ViewPost. Then this action method is not going to receive the year and the month any more, but only the url (and optionally status). Here is the updated action method.

public ActionResult ViewPost(string url, string status)
{
    var allPosts = GetPostsInternal();
    var currentPost = allPosts.SingleOrDefault(p => p.PostUrl == url && p.EntryType == 1);
 
    if (currentPost == null)
    {
        throw new UrlNotFoundException("Unable to find a post w/ the url {0}", url);
    }

    // -- Cut for brevity --     

    return View(model);
}

The next step is to change the file name of sBlog.Net\Views\Home\View.cshtml to ViewPost.cshtml. And here is the updated content for this file.

@model sBlog.Net.Models.ViewPostOrPageModel
 
@if (Model.PreviousPost != null)
{
    <div style="float: left">
        @Html.RouteLink("<< Previous Post", "IndividualPost", new { url = Model.PreviousPost.PostUrl, status = string.Empty }, new { @title = Model.PreviousPost.PostTitle })
    </div>
}
 
@if (Model.NextPost != null)
{
    <div style="float: right">
        @Html.RouteLink("Next Post >>", "IndividualPost", new { url = Model.NextPost.PostUrl }, new { @title = Model.NextPost.PostTitle })
    </div>
}

Finally, update the sBlog.Net\Views\Shared\Post.cshtml file to present the new url format for the title link for the post.

<div class="post">
    @{
        var url = Url.RouteUrl("IndividualPost", new { url = Model.Post.PostUrl, status = string.Empty });
    }
    
    ....
    
</div>

Hope this post was interesting! If you do have any questions/ comments post them here!


<< Previous Page