.NET and Open Source: better together


Writing extensions for BlogEngine 1.4 (part 4)


Default admin interface for extensions in BlogEngine works fine in most cases and very easy to use. But sometimes you just got to get creative, right? That means, you want no limits. Obviously, some of simplicity will be lost – but still it is surprisingly easy to get along using plug-and-play BlogEngine architecture. Lets say, we ran into nice DHTML color picker and want to use it in the admin page for our extension. We want admin page look like this picture – it should allow us to enter a word, size and color. This values will be saved in the extension manager, so we don’t have to deal with databases or file system – usual BE stuff. And when you click “Color picker” button, nice color picker will show up and you can visually select value for a color.

ex_5-2 As you can imagine, few non-standard issues here. Control itself comes with java scripts, CSS and images. So first thing first – we download control and put all files in the “Example_05” folder in the blog’s root. Then we need to create custom admin page (Admin.aspx) to host admin interface. It inherits from admin master page, so it looks like part of the control panel. It is custom – you can use any controls ASP.NET let you to use in the page, any third party controls etc. In this case, we’ll use standard ASP.NET controls and then call color picker function on button click.

<%@ Page Language="C#" MasterPageFile="~/admin/admin1.master" AutoEventWireup="true" CodeFile="Admin.aspx.cs" Inherits="audio_Admin" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="cphAdmin" Runat="Server">
<br />
<div class="settings">
    <h1>Settings: Example_05</h1>
    <div id="formContainer" runat="server" class="mgr">
        <div id="lblWord" style="width:120px; height:20px; float:left;" runat="server"></div>&nbsp;
        <asp:TextBox ID="txtWord" runat="server" MaxLength="6"></asp:TextBox>
        <br />
        <div id="lblSize" style="width:120px; height:20px; float:left;" runat="server"></div>&nbsp;
        <asp:TextBox ID="txtSize" runat="server" MaxLength="6"></asp:TextBox>
        <br />
        <div id="lblColor" style="width:120px; height:20px; float:left;" runat="server"></div>&nbsp;
        <asp:TextBox id="rgb2" runat="server" />
        <asp:Button id="btnColPicker" runat="server" text="Color picker" />
        <br />
        <br />
        <div style="margin-left:128px">
            <asp:Button ID="btnSave" runat="server" onclick="btnSave_Click" />

Server-side code is simple, with a few useful tricks. We add client click event to ASP.NET button to call JavaScript function to show color picker. Then we programmatically reference JavaScript and CSS files in the code and add them to the page header. On page load and button save click we do usual Extension Manager technic loading and saving values using data store.

public partial class audio_Admin : System.Web.UI.Page
    static protected ExtensionSettings _settings = null;
    protected void Page_Load(object sender, EventArgs e)
      btnColPicker.OnClientClick = "showColorPicker(this," + rgb2.ClientID + "); return false;";
      if (!Page.IsPostBack)
        _settings = ExtensionManager.GetSettings("Example_05");
    protected void btnSave_Click(object sender, EventArgs e)
      _settings.UpdateScalarValue("word", txtWord.Text);
      _settings.UpdateScalarValue("size", txtSize.Text);
      _settings.UpdateScalarValue("color", rgb2.Text);
        ExtensionManager.SaveSettings("Example_05", _settings);
    protected void BindForm()
      btnSave.Text = Resources.labels.saveSettings;
      txtWord.Text = _settings.GetSingleValue("word");
      txtSize.Text = _settings.GetSingleValue("size");
      rgb2.Text = _settings.GetSingleValue("color");
  private static void BuildHeader()
    // get a page handler
    System.Web.UI.Page pg = (System.Web.UI.Page)HttpContext.Current.CurrentHandler;
    // check if script already added to the page header
    foreach (Control ctl in pg.Header.Controls)
      if (ctl.GetType() == typeof(HtmlGenericControl))
        HtmlGenericControl gc = (HtmlGenericControl)ctl;
        if (gc.Attributes["src"] != null && gc.Attributes["src"].Contains("color_functions.js"))
        if (gc.Attributes["src"] != null && gc.Attributes["src"].Contains("js_color_picker_v2.js"))
        if (gc.Attributes["href"] != null && gc.Attributes["href"].Contains("js_color_picker_v2.css"))
    HtmlGenericControl js = new HtmlGenericControl("script");
    js.Attributes.Add("type", "text/javascript");
    js.Attributes.Add("src", "../Example_05/color_functions.js");
    HtmlGenericControl js2 = new HtmlGenericControl("script");
    js2.Attributes.Add("type", "text/javascript");
    js2.Attributes.Add("src", "../Example_05/js_color_picker_v2.js");
    HtmlGenericControl css = new HtmlGenericControl("link");
    css.Attributes.Add("rel", "stylesheet");
    css.Attributes.Add("type", "text/css");
    css.Attributes.Add("href", "../Example_05/js_color_picker_v2.css");

Extension itself pretty dumb – it runs on comment serving and decorates word with CSS style according to the size/color of your choosing. The important line is:

ExtensionManager.SetAdminPage("Example_05", "~/Example_05/Admin.aspx");

Here you tell BlogEngine to use your custom admin page instead default one, generated by Extension Manager. Everything else is as simple as it gets.

[Extension("Example_05", "1.0", "<a href=\"http://me.net\">Me</a>")]
public class Example_05
  static protected ExtensionSettings _settings = null;
  public Example_05()
    Comment.Serving += new EventHandler<ServingEventArgs>(Post_CommentServing);
  private static void Post_CommentServing(object sender, ServingEventArgs e)
    string body = e.Body;
    string colored = "<span style='font-size:{0}px; color:{1}'>{2}</span>";
    string word = _settings.GetSingleValue("word");
    string size = _settings.GetSingleValue("size");
    string color = _settings.GetSingleValue("color");
    if(word.Length > 0)
      body = body.Replace(word, string.Format(colored, size, color, word));
    e.Body = body;
  protected void InitSettings()
    ExtensionSettings settings = new ExtensionSettings("Example_05");
    settings.AddValue("word", "BlogEngine");
    settings.AddValue("size", 12);
    settings.AddValue("color", "000000");
    settings.IsScalar = true;
    // set page that Extension Manager will use  
    // instead of default settings page
    ExtensionManager.SetAdminPage("Example_05", "~/Example_05/Admin.aspx");
    _settings = ExtensionManager.GetSettings("Example_05");

ex_5-1 If we've done everything correctly, we should be able to go to any post and, if it has word “BlogEngine” in it, this word will have size (in pixels) and color specified in our custom made admin page. You can always go to Admin/Settings select Example_05 extension from the list, change any values on the form and comments will reflect this change right away. As you can see, creating custom admin interface for extension in BlogEngine is definitely not a rocket science. All you need is make your hand-crafted page and let BlogEngine know about it – simple like that!

Download extension files here: Example_05.zip (24.96 kb)

Comments (1) -

Nice tutorials, thanks!

Comments are closed
Picasa SlideShow for BlogEngine


.NET and Open Source: better together


Picasa SlideShow for BlogEngine

picasa-5 I'm a long time Picasa fan. I used it before it was bought by Google and even before I new what the Google is. Now at version 3.0, although not as popular as Flickr, Picasa is a great choice as desktop photo manager and it's Web Albums is a valuable online photo storage. Web albums itself is nice classy web application with pretty good flash slide show. You can even get a code to embed slide show (or photo) into your own blog, which works great for occasional use, but can be daunting if you plan regular photo posts.

My master plan was:

  • load photos from camera to Picasa desktop
  • edit and upload to web album
  • from BlogEngine, select any of my Picasa albums
  • add slide show of this album to the post

picasa-4 API for Picasa is a part of Google web API. You can easily find many examples on how to use Picasa API to show pictures on your blog. Virtually all such examples on how to show public photos and this is understandable - blog is a public place. What I wanted is to be able to also show private albums on members only blog, and googling around wasn't much help. So I had to dig a little bit myself to figure this one out.

picasa-3 Newly updated security model for Picasa has 3 levels: public, unlisted and private. Public is what it is, to see private albums you need to be logged in into Google through one of its services, like Gmail. The most interesting one is "unlisted". It implements sort of "security by obscurity" model for private photos. It sounds bad, but it gives you huge advantage: you can send URL to your private album to anybody as a link in email and they can come and look at photos without having to authenticate yourself. Very convenient, and people love it. The trick is that part of URL is an authentication token, which is generated by service and unique for every photo album. That means, to crack down to your private album one should know your Google account and then run some sort of dictionary attack to get authentication key, and Google (I hope) monitors such activity, so for all practical purposes you do have your private folder locked by password. And this password is what you passing around when inviting friends to look at pictures of your new-born baby or what have you. Pretty clever, I guess.

picasa-2 May be it is part of "obscurity" thing, but Google API does not tell you much about this authentication token, and there is no property (at least I wasn't able to find one) that holds it in the object model. The authentication section in API documentation is a little confusing, IMHO. So I had to hack it out on my own by examining values used to send/receive requests. I was able to get it from feed URI, there are other ways too but this one seems to be easy enough:

foreach (PicasaEntry entry in feed.Entries){
  string authKey = ((Google.GData.Client.AtomEntry)(entry)).FeedUri;
  authKey = authKey.Substring(authKey.IndexOf("authkey=")).Substring(8);

Now I can generate tag to embed flash object into the post, rest is trivial. One nice touch I wanted is to be able to copy generated by extension tag to clipboard by clicking it's hyperlink. It is very easy in IE, but somewhat nightmarish in other browsers. I had to use well known workaround with flash used to actually copy string to clipboard. Should work in IE and FF, if for any reason it doesn't for you - just manually copy tag and paste it in the post where you want slide show to appear. I also toyed a little with alternative idea - select post in drop down and then on button click append generated tag to that post, but decided to put it aside for now. May be one of the features in the next release ;)


  1. Download, unzip and copy files to corresponding locations.
  2. Log into your blog and go to "Extensions" under "Administration" menu.
  3. Click settings/edit to get to PicasaSlideShow settings page.
  4. Fill in your Gmail ID/password and change any available settings if you don't like defaults.
  5. Save settings, now list of your Picasa albums should appear in the second section of the page.
  6. Click generated tag (right column) to copy it to clipboard (or copy it manually).
  7. Create new or edit existent post or page and paste Picasa tag where you want slide show to appear.


This extension uses 3 Google client DLLs, and it runs fine on my host in medium trust. Try it - may be you'll like it. I will keep looking into Google APIs to see what can be there for BlogEngine and will likely enchance this extension over time.

PicasaSlideShow.zip (94.68 kb)

Comments (9) -

Very cool. Thanks for sharing. Unfortunately all of my photos are on Flickr! Maybe I can transfer some of them over...

Very nice and thanks for sharing.  I just wish they allowed you to store more pictures online.

Geez man... I just spend about 3 days working on a Picasa extension and I find this.  :)  Well mine is different, eventually I'll clean it up enough to release it!

I tried your Picasa extension and had problems.  So I removed it and instead tried this PicasaSlideShow extension instead.  But this does not work either.  I noticed in your post something about Medium Trust.  That sounds familiar, but not sure what it is or if my hosted environment is working under Medium Trust, but this PicasaSlideShow does not work either, but I do get a more detailed error message.

Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

Line 68:         if (t.Length > 1) key = "authkey%3D" + t[1];
Line 69:
Line 70:         string user = _settings.GetSingleValue("Account");
Line 71:
Line 72:         string auto = "";

Source File: App_Code\Extensions\PicasaSlideShow.cs    Line: 70

JR, try to add this line just before line #70:

if (_settings == null) InitSettings();

Thats Great!  That fixed it.  Thank You!

Is there another quick fix for the other Picasa extension?


United States Laura Durand

I have a question I was hoping you could answer. I have a public side show from a tour I just lead...I want to allow all the people from the group to load photos to the album. Is it possible to create a new id and password so they won't have access to my gmail account?

Is it possible to create a new id and password so they won't have access to my gmail account?
If I understood you correctly, the only way for you to go is to create separate gmail account and share it with group so anybody can use it. To provide "blind" upload so that someone can drop their photos without knowing gmail id/password would require some extra functionality that neither Picasa web albums nor my extension provides. But it is an interesting point, may be I should add this to extension in the next version.

Comments are closed