Visual Studio Solution Uploader – Live Writer Plugin

Over the past few days I have been creating a Visual Studio Solution Uploader as a Live Writer plugin. The plugin allows you to select a Visual Studio 2005 solution file, when selected the solution and the directory will be zipped and included in the blog post. All done in a few simple steps. If you wish, there is also the option to build the solution and have them also uploaded as a separate zip file.

This is an early release as I want to get some feedback about what you think, there is more than likely to be one or two bugs. I have found it really useful, no longer do I have to manually zip and upload samples along with the blog, I can just get my plugin to do it all for me!

Download Installer

The plugin uses the same settings as Windows Live Writer for publishing images, so if your blog provider doesn’t support uploads you will need to have your FTP configured.

Big thanks to Scott who helped me get my settings correct.

Screenshots

image

image

image

Any questions or comments, please let me know.

(Update: I blogged this on the 3rd but I uploaded it to the wrong blog!…. Sorry).

Technorati tags: , ,

Windows Live Writer Plugin – Installation

After you have created your amazing live writer plugin, you will need to actually install the plugin on the users machine.

In this post, I will discuss how to create an installer (msi package) for your plugin which you will be able to provide to your users.

I will be using the Setup Project template within Visual Studio 2005.  I would have used WiX but I think it’s a lot more complex to setup initially.  One day, I might look at WiX in more detail.

image

After you select the project, you will be shown a File System window with the installer config files in your solution. The installation allows you to place files in the Applications folder, the user’s desktop and the user’s program menu.  If you click right the solution project, you can view different parts of the installer.  For example, you can select Registry to make changes to the registry during the installation.

image

In the above dialog, if you add a file to the Application Folder, then when the installer is executed, the file will be placed there.

When creating the setup, the first thing to do is set where you want the installer to put all the files. In this case, it will be the plugin directory for Windows Live Writer.  Note, in Beta 3 this has moved.

Within the properties for the Application Folder, set the DefaultLocation to

[ProgramFilesFolder]Windows LiveWriterPlugins

image 

This simply says, place all the files in the user’s program filesWindows LiveWriterPlugins.

The next thing to do is to add the files. Right click in the blank Application Folder and select Add > Project Output.  This takes the output from the other projects in the solution and includes it in the setup.

image

However, there are a number of assemblies listed which we don’t want to install (they are referred by the API, but don’t require deployment).  Highlight all the files you don’t want, go to the properties dialog and set the Exclude property to True. 

image

The files will then be removed from the Application Folder.  If you look in Solution Explorer, you can see that it has listed all the Dependences, however has excluded the selected ones.

image

You can now build the project and the output text should be similar to below.

—— Starting pre-build validation for project ‘LiveWriterInstaller’ ——
—— Pre-build validation for project ‘LiveWriterInstaller’ completed ——
—— Build started: Project: LiveWriterInstaller, Configuration: Debug ——
Building file ‘F:UsersBen HallDocumentsVisual Studio 2005ProjectsLiveWriterInstallerDebugLiveWriterInstaller.msi’…
Packaging file ‘writer.png’…
Packaging file ‘WindowsLiveWriterInstallation.dll’…
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

From the output, you can see that it has created us a msi file.  This is what we need to give to our users in order for them to be able to install the plugin.

When we launch the MSI, it will look just like a normal installer.

image

As you can see from the screenshot below, the location for where the installer will copy the files too is set correctly by default.

image

As for repair and uninstall part of the installer, they come for free by default.

You can now upload the MSI to your blog / Live Gallery for everyone to enjoy.  This has just been a quick overview, but it should be enough to get you started.

Technorati tags:

Windows Live Writer Plugin – Uploading files

In this post, I will discuss how you can create a Windows Live Writer plugin, that allows a user to attach files to a blog post, when the user clicks publish the file will be uploaded via FTP, or directly using your blog provider if its supported.  Live Spaces, LiveJournal and Blogger do not support uploading files, in this case FTP will need to be setup via the Tools > Options > Accounts > Select and Edit > Images > Configure FTP.  This plugin will work the same as when inserting images onto a blog.

The plugin isn’t going to do anything special, I’m sure you will be able to use the other posts i’ve wrote to create something special.

To have access to adding files to the blog, your plugin will need to inherit from SmartContentSource and override CreateContent, CreateEditor and GeneratePublishHtml.  On this post, I’m only going to implement GeneratePublishHtml functionality.

 Below is all the code required to add a file to the blog. 

public override string GeneratePublishHtml(ISmartContent content, IPublishingContext publishingContext)
{
    string filename = “ZipTest.zip”;
    string fullpath = “F:ZipTest.zip”;
    content.Files.Add(filename, fullpath);
    string filePath = content.Files.GetUri(content.Files.Filenames[0]).ToString();
    return string.Format(“

Download File – {1}

“, filePath, filename);
}

content.Files.Add takes a filename and a full file path to the file which you wish to upload.  When you add the file, LiveWriter copies the file to a temporary location, by using content.Files.GetUri() we can gain access to this temp file path.

We then return the html code to insert a download link to the file into the post. 

There is a problem, when inserting the link into the blog, the link is to your local directory – not very useful on your blog.  If you just return plain text from GeneratePublishHtml then LiveWriter will just post the local path text.  However, if you include the URL within a link tag then it replaces the path with a $ (It will become

Download File – ZipTest.zip

). 

When you click publish, the file will be uploaded to the web server and the $ will be replaced with the path to the file on the web server.  One problem with this is that it uses the path set in the options dialog, which is used for the images.  If you upload any files, they will be put in the same directory as your images.  It would have been great to be able to set which directory the files ended up in – maybe a separate dialog setting – or just use a directory called Files instead of Images.

Download – LiveWriterUploader.cs.txt

Technorati tags:

Windows Live Writer Plugin – Adding an icon

So something I forgot to mention so far is now to add an icon to your plugin.

The image file must be a bitmap (png) and be 20×18.  Below is the correct format for a plugin icon, the image is the LiveWriter icon.  I haven’t add transparency, but you could if you wanted.

writer 

To add an icon, first include it in the VS solution and set the Build Action on the file properties to be an Embedded Resource and the Copy to Output property to Copy Always.

Then, in the WriterPlugin attribute, set the ImagePath property to be the image filename “writer.png”. If the image is in a sub directory, then you need to include the foldername in the filename. 

For example ImagesWriter.png would be Images.Writer.png

Technorati tags:

Windows Live Writer Plugin – PluginDiagnostics

Following on from my previous post on debugging, the API has a class called PluginDiagnostics.

This has got a series of methods for displaying and logging error messages from within your plugin.

In this post, I am going to use it to display an error to the end user and log the error message.

The method PluginDiagnostics.DisplayError displays an error message. PluginDiagnostics.LogError will log the error message. 

The log file location on Vista is %LOCALAPPDATA%Windows Live Writer,  while on XP is it %USERPROFILE%Local SettingsApplication DataWindows Live Writer.

Below is some sample code which displays and logs the error.

public override DialogResult CreateContent(IWin32Window dialogOwner, ref string content)
{
    content = “Test PluginDiagnostics”;
    string errorTitle = “Error has occured: Code = 129453ABC”;
    string errorMessage = “Sorry but this plugin doesn’t work correctly. Report @ Blog.BenHall.me.uk”;
    PluginDiagnostics.DisplayError(errorTitle, errorMessage);
    PluginDiagnostics.LogError(errorMessage);

    return DialogResult.OK;
}

The error message displayed by the method looks like this:

image 

The contents of the log file would be this:

WindowsLiveWriter,1.1444,None,00004,23-Sep-2007 11:36:22.202,”Error has occured: Code = 129453ABC: Sorry but this plugin doesn’t work correctly. Report @ Blog.BenHall.me.uk”,””
WindowsLiveWriter,1.1444,None,00005,23-Sep-2007 11:36:22.237,”DisplayableException occurred: WindowsLive.Writer.CoreServices.DisplayableException: Error has occured: Code = 129453ABC – Sorry but this plugin doesn’t work correctly. Report @ Blog.BenHall.me.uk”,””
WindowsLiveWriter,1.1444,None,00006,23-Sep-2007 11:36:23.491,”Sorry but this plugin doesn’t work correctly. Report @ Blog.BenHall.me.uk”,””

Very useful class, plus the error message keeps it in the style of live writer for a consistent end user experience.

Technorati tags:

Windows Live Writer Plugin – Project Template

Today, as I was writing another plugin example I got really bored setting up the properties, attributes etc so I created a VS project template.

Download it here – WindowsLiveWriterPlugin.zip

To install, you need to put the zip inside PATHTOUSERPROFILEDocumentsVisual Studio 2005TemplatesProjectTemplates

It will then display in your project creation.

image

Hope you find it useful to get you up and running.

Technorati tags:

Problem with Debugger.Break();

While writing my previous post on how to debug live writer plugins, I mentioned using System.Diagnostics.Debugger.Break();  However, I found a problem with this. If you have a debugger attached, for example Visual Studio, then the program will hit the line of code and the debugger will step in. Great!

However, if no debugger is attached, the following error occurs:

image

I instantly thought this was strange, so built it as a Release version. Same thing happened. I know that with Debug.Write() the calls are removed in Release mode to save time, space and so no strange errors like this occurs.

First thing I did was boot up Reflector.  The debug class uses the ConditionalAttribute which provides a way to include/exclude methods depending on the preprocessor symbol.  So, if the Conditional compilation symbol is set to DEBUG then the method is included in the build, otherwise all the calls are removed.

[Conditional(“DEBUG”)]

However, if we look at Debugger.Break(), it doesn’t use a ConditionalAttribute which means we cannot take advantage of this compiler feature.  The result of this is that you need to manually remove all the lines of code which calls Debugger.Break() before shipping the code to the client, or when not running it with a debugger attached (like running unit tests). Not great at all!!

Strange how this got missed, unless it was done for a reason?? 

Technorati tags: , ,

Windows Live Writer – Debugging and Unit Testing

Over the last few posts I have been talking quite high level, not requiring any debugging or unit testing however in a real plugin these things would be required during development. In this post, I will talk about how to debug a plugin and also how to use MbUnit to create unit tests for your plugin.

For this post, I’m going to use the plugin I created in my previous post which saved information into the global plugin option properties.

As the solution is a class library, we can’t simply hit F5 and jump straight into the debugger.  All the interacts with the plugin are done via LiveWriter so you need some way to access the process and the plugin.

On MSDN, the article which quickly outlines how to create a plugin has at the bottom “Build your plugin and then run Windows Live Writer to test and debug.”

So let’s give it a go.  In the Initalize method of the plugin, set a breakpoint in VS or use the Debugger.Break() command.

public override void Initialize(IProperties pluginOptions)
{
            System.Diagnostics.Debugger.Break();
            options = pluginOptions;
            base.Initialize(pluginOptions);
}
 

Then, go into the properties of the project > debug > Start Action > Start External Program > Set it to “C:Program FilesWindows Live WriterWindowsLiveWriter.exe”.

Hit F5 and Live Writer will launch.  Select to insert the Options plugin (or the one you are trying to debug) and the Visual Studio debugger will kick in.  Great!! You can now easily debug your plugin like a normal application.

So we can now debug, but how about unit testing?  Well, it’s just a standard C# class library so we can create a Plugin.Tests library, reference MbUnit.Framework.dll and your Plugin.dll and you are away!

Your test could then look something like this.

[TestFixture]
public class Tests
{
    [Test]
    public void ContentCreatedAsExpected()
    {
        OptionsPlugin op = new OptionsPlugin();
        op.Initialize(new PropertiesStub());
        string actualContent = string.Empty;
        DialogResult actualResult = op.CreateContent(null, ref actualContent);
        Assert.AreEqual(actualResult, DialogResult.OK);
        Assert.AreEqual(actualContent, “Option not set”);
    }
}

You may notice, I’m using a PropertiesStub object to replace the IProperties parameter.  We need some implementation in place for our plugin code to work. So what I did was just create a simple stub to save and return values.

public class PropertiesStub : IProperties
{
    Dictionary h = new Dictionary();
    public string GetString(string name, string defaultValue)
    {
        if (h.ContainsKey(name))
            return h[name];
        else
            return defaultValue;
    }

    public void SetString(string name, string value)
    {
        if (h.ContainsKey(name))
            h[name] = value;
        else
            h.Add(name, value);
    }

….

}

You would have to follow all the general advice for TDD (like separate logic and UI) in order to have a good test base. Hopefully, you can see that it is easy to both debug and unit test.

Download Unit Test Sample – LiveWriterOptions.Tests.zip

Technorati tags:

Windows Live Writer Plugin – Options

In this post, I will talk about setting options, or preferences, for your plugin.  This is different to the properties as it relates to the plugin on a global/shared level instead of just on the content-specific level in the blog post.

If you haven’t read my first post, then that would be a good place to start.  To start with, you need to create the standard C# class library, reference the API and System.Windows.Forms, finally creating the attribute.

The first task is to set the HasEditableOptions = true in the WriterPluginAttribute. This will then enable the Options button in Live Writer’s preference tab for the plugin.

The second task is to override the EditOptions menu which is executed when the button is clicked.

public override void EditOptions(System.Windows.Forms.IWin32Window dialogOwner)
{
   OptionsForm of = new OptionsForm();
   of.Show();
}

OptionsForm is a simple WinForm.

Now we have the basic structure in place, we need some way, and place, to save and access the options set.  Because this is global, we cannot use the ISmartContent.Properties like we did in my previous post.

The API does offer a IProperties object which can be used to store and receive settings for the plugin.  This works in a similar fashion to ISmartContent.

To start with, we need to gain access to the object.  It is actually passed into an Initialize method when the plugin is created, we can simply override this method and store a reference to the object, before calling the base method.

private IProperties options = null;
public override void Initialize(IProperties pluginOptions)
{
    options = pluginOptions;
    base.Initialize(pluginOptions);
}
  

On the OptionsForm, I override the constructor to accept a IProperties object.

private IProperties options = null;
public OptionsForm(IProperties options)
{
    this.options = options;
    InitializeComponent();
}

Now I have access to the properties, I add a textbox and a button onto the form and hook up the saving of the value.

private void button1_Click(object sender, EventArgs e)
{
    options.SetString(“SettingsValue”, textBox1.Text);
    Close();
}

Finally, we need to output something.  Because we are inheriting from a ContentSource object, we need to override CreateContent.  I simple try to output the contents of the TextBox to the blog post.

public override DialogResult CreateContent(IWin32Window dialogOwner, ref string content)
{
    content = options.GetString(“SettingsValue”, “Options not set”);
    return DialogResult.OK;
}

One last change, in the constructor of the form, we need to populate the values of the existing settings

textBox1.Text = options.GetString(“SettingsValue”, “”);

And that’s it, you now have an option which is stored for the lifetime of the plugin which can be accessed when inserting content.

Tip: Wrap the options with your own class to provide strongly typed access.

Download Solution – LiveWriterOptions.zip

Technorati tags: