BlogEngine.NET Modifications for Mono/Linux

NOTE: This page is outdated. BlogEngine.NET now works on Mono out of the box as I've merged these changes into the main project.


In the Web.config, removed the description part from these lines (not entirely sure if it was necessary any more):


<add name="XmlMembershipProvider" type="BlogEngine.Core.Providers.XmlMembershipProvider" description="XML membership provider" xmlFileName="~/App_Data/users.xml"/>
<add name="XmlRoleProvider"  type="BlogEngine.Core.Providers.XmlRoleProvider"  description="XML role provider" xmlFileName="~/App_Data/roles.xml"/>
<add name="PageSiteMap" description="The site map provider that reads in the .sitemap XML files." type="BlogEngine.Core.Web.Controls.PageSiteMap"/>
<add name="SecuritySiteMap" description="Used for authenticated users." type="System.Web.XmlSiteMapProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" securityTrimmingEnabled="true" siteMapFile="web.sitemap" />

commented out the <connectionStrings configSource="sql.config" /> from the Web.Config and added the contents of the referred file to the web.config.

  <connectionStrings>
    <add name="BlogEngine" connectionString="Data Source=MySQLServer;User ID=user;Password=password;persist security info=False;initial catalog=BlogEngine;" providerName="System.Data.SqlClient"/>
    <add name="LocalSqlServer" connectionString="Data Source=MySQLServer;User ID=user;Password=password;persist security info=False;initial catalog=BlogEngine;" providerName="System.Data.SqlClient"/>
  </connectionStrings>

The LocalSqlServer entry is needed for some reason.


Added an assembly reference to all type specifications in the Web.config file.

In other words, a line like type="BlogEngine.Core.Providers.XmlRoleProvider" becomes type="BlogEngine.Core.Providers.XmlRoleProvider, BlogEngine.Core".

You should do that for all type references or Mono complains that it can't find the type.




commented out these lines in XmlMembershipProvider.cs

          if (string.IsNullOrEmpty(config["description"]))
          {
            config.Remove("description");
            config.Add("description", "XML membership provider");
          }

commented out these lines in XmlRoleProvider.cs

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "XML role provider");
            }


Removed the CSS compression by adding the following line to BlogBasePage.cs

        protected virtual void CompressCss()
        {
                    return; // TODO this doesn't work in mono, commented it out

            if (Request.QueryString["theme"] != null)
                return;
            
            foreach (Control control in Page.Header.Controls)
            {
                HtmlControl c = control as HtmlControl;
                if (c != null && c.Attributes["type"] != null && c.Attributes["type"].Equals("text/css", StringComparison.OrdinalIgnoreCase))
                {
                    if (!c.Attributes["href"].StartsWith("http://"))
                        c.Attributes["href"] = Utils.RelativeWebRoot + "themes/" + BlogSettings.Instance.Theme + "/css.axd?name=" + c.Attributes["href"];
                }
            }
        }

Mono has a compiler bug causing invalid IL exceptions for the generic base types. To get around it, do the following:

Add the following to BlogEngine.Core.Post:

        public new static Post Load(Guid id) {

            Post instance = new Post();
            instance = instance.DataSelect(id);
            instance.Id = id;
            if (instance != null) {
                instance.MarkOld();
                return instance;
            }
            return null;

        }

Add the following to BlogEngine.Core.Page:

        public new static Page Load(Guid id) {

            Page instance = new Page();
            instance = instance.DataSelect(id);
            instance.Id = id;
            if (instance != null) {
                instance.MarkOld();
                return instance;
            }
            return null;
        }

Add the folling to BlogEngine.Core.Utils

        public static string StripLeadingSlash(string input) {
            return (input.StartsWith("/")) ? input.Remove(0, 1) : input;
        }

        public static string IncludeLeadingSlash(string input) {
            return (input.StartsWith("/")) ? input : "/" + input;
        }

Replace ConvertToAbsolute in BlogEngine.Core.Utils with the following:

        public static Uri ConvertToAbsolute(Uri relativeUri) {
            if (relativeUri == null)
                throw new ArgumentNullException("relativeUri");
            string absolute = AbsoluteWebRoot.ToString();
            int index = absolute.LastIndexOf(RelativeWebRoot.ToString());
            return new Uri(absolute.Substring(0, index) + IncludeLeadingSlash(relativeUri.ToString()));
        }

Modify BlogEngine.Core.Post and replace the RelativeLink property with the following:

        public Uri RelativeLink {
            get {
                string slug = Utils.RemoveIllegalCharacters(Slug) + BlogSettings.Instance.FileExtension;
                if (BlogSettings.Instance.TimeStampPostLinks) {
                    return new Uri(Utils.StripLeadingSlash(Utils.RelativeWebRoot) + "post/" + DateCreated.Year + "/" + DateCreated.ToString("MM") + "/" + slug, UriKind.Relative);
                }
                return new Uri(Utils.StripLeadingSlash(Utils.RelativeWebRoot) + "post/" + slug, UriKind.Relative);
            }
        }

Add the following to BlogEngine.Core.Post:

        public string RelativeLinkString {
            get {
                return Utils.IncludeLeadingSlash(RelativeLink.ToString());
            }
        }


Modify BlogEngine.Core.Page and replace the RelativeLink property with the following:

        public Uri RelativeLink {
            get { return new Uri(Utils.StripLeadingSlash(Utils.RelativeWebRoot) + "page/" + Utils.RemoveIllegalCharacters(Title) + BlogSettings.Instance.FileExtension, UriKind.Relative); }
        }

At this point you should be able to run in XSP on Windows.


In the admin/Pages folder, go through each aspx file and replace the reference to the master page with "~/admin/admin1.master"

Commented out the line "Page.Form.DefaultButton = btnSave.UniqueID;" from admin/Pages/Add_Entry.cs and admin/Pages/Pages.cs. This throws an exception in Mono. Not sure if there is a workaround.

Had to replace

post.DateCreated = DateTime.ParseExact(txtDate.Text, System.Globalization.CultureInfo.InvariantCulture).AddHours(-BlogSettings.Instance.Timezone);

with

post.DateCreated = DateTime.ParseExact(txtDate.Text, "yyyy-MM-dd HH:mm", null).AddHours(-BlogSettings.Instance.Timezone);

in Add_Entry.cs as the code caused an invalid date exception. It shouldn't make a difference as the date is implicitly formatted like that when it is added to the form.

Removed the StringComparison.Ordinal parameter from the LastIndexOf calls in BlogEngine.Core.Web.HttpModules.UrlRewrite.ExtractTitle


        private static string ExtractTitle(HttpContext context) {
            int index = context.Request.RawUrl.ToLowerInvariant().LastIndexOf("/") + 1;
            int stop = context.Request.RawUrl.ToLowerInvariant().LastIndexOf(BlogSettings.Instance.FileExtension);
            string title = context.Request.RawUrl.Substring(index, stop - index).Replace(BlogSettings.Instance.FileExtension, string.Empty);
            return context.Server.UrlEncode(title);
        }

Replace all references to RelativeLink.ToString() with RelativeLinkString. This may end up only working if your site is hosted at the root. You may have to resolve these references to a full absolute url in order for them to work with non-root sites.

If you view a post, you'll notice that your theme's CSS is not displayed. You'll have to hard code the path in the theme's site.master page as mono does not resolve the paths correctly by itself. You may want to have it set server side.

Add the following to your theme's site.master.cs Page_Load method:

      HlHome.NavigateUrl = BlogEngine.Core.Utils.AbsoluteWebRoot + "default.aspx";
      HlArchive.NavigateUrl = BlogEngine.Core.Utils.AbsoluteWebRoot + "archive.aspx";
      hlContact.NavigateUrl = BlogEngine.Core.Utils.AbsoluteWebRoot + "contact.aspx";

Mono does not resolve those methods correctly by itself (due to the URL rewriting I suspect).

Next step is to go through all the pages and correct the inconsistant casing. Linux paths are case-sensitive, so this is a real problem.

Search for "app_data" and correct it to be "App_Data".
Rename your "Bin" folder to lowercase "bin". Mono does not recognize the uppercase version.

Fix the lowercase "web.sitemap" line in web.config to be "Web.config"
Fix the uppercase "Users.xml" line in XmlRoleProvider.cs to be "users.xml".

Search and replace all miss-cased occurrances of "Add_entry.aspx".

Commented out the BindCultures method in admin/Pages/Settings.aspx.cs. This was causing a culture not supported exception on linux.

Changed BindThemes to use Path.DirectorySeparatorChar rathe than "\\" in admin/Pages/Settings.aspx.cs.

Had to change all references to rssbutton.gif to update their relative roots (side effect of having to change the Utils.RelativeWebRoot implementation).
Also added a new lowercase rssbutton.gif as that is how it is referenced throughout the project.

Removed the Compression HttpModule from the Web.Config as it was causing question marks to appear at the beginning of every page.

 


I am a software developer / architect currently interested in combining .NET technologies with open-source operating systems. 

I am a member of the open-source BlogEngine.NET development team and focus mainly on ensuring Mono compatibility for the project.

twitter


At StayUnlimited Cape Town accommodation we help you choose from and book guest houses, self catering apartments, bed & breakfasts, luxury villas and hotel accommodation.