Comicster Plug-Ins

Comicster 4 uses Autofac's MEF integration to allow for third-party plug-ins. In this post I'll be describing the various extensibility points in Comicster and how you can write your own plug-ins.

This document is a work in progress, and eventually I may move it to the official Comicster website (once I've updated that site to reflect the new version).

Writing and Naming Your Plug-In

To write a plug-in, create a new "Class Library" project in Visual Studio, add a reference to Comicster.Core.DLL, and copy your compiled DLL into a "Comicster" folder under your "My Documents" folder. Restart Comicster and it'll discover any new plug-ins in that folder.

By convention, all "first party" plug-ins (that is, those that I create) will be named Comicster.Type.Name.dll. For example, "Comicster.Tools.CmxMerger", "Comicster.IO.Cmx" or "Comicster.Searches.ComicAges".

For third-party plug-ins, I recommend a similar approach, except use your own name/company/project as the first part of the name. So if I were to publish a "third party" plug-in under the MadProps guise, I'd call it MadProps.Tools.Foo.dll or MadProps.Catalogs.Bar.dll.

Plug-In Types

File Format Plug-Ins

Comicster File Plug-Ins

Comicster makes two interfaces available for you to implement if you want to support a custom file format for collections. They live in the Comicster.IO namespace and look like this:

public interface ICollectionReader : ICollectionFileHandler
{
    /// <summary>
    /// The file extension (including the ".") used by this file format
    /// </summary>
    string Extension { get; }

    /// <summary>
    /// The Filter used in FileDialogs to represent this file format
    /// </summary>
    string Filter { get; }

    Collection ReadCollection(Stream stream);
}

public interface ICollectionReaderWriter: ICollectionReader
{
    void WriteCollection(Stream stream, Collection collection);
}

So the idea is that if you want to make a new "read only" file format plug-in (like the one we use to open CMX files from the previous version) you only need to implement ICollectionReader so you can return a new Collection instance from the given stream.

If you want to be able to save to your custom file format, you should implement ICollectionReaderWriter and provide a way to write a collection to a stream too.

Online Catalog Plug-Ins

Comicster Catalog Plug-Ins

Comicster lets you search online catalogs for details of items in your collection, and merge them into your local file. By default it ships with the ability to search its own online database (at comicster.net), but there's an interface you can implement if you want to define your own catalogs. It's in Comicster.Catalogs:

public interface ICatalog
{
    /// <summary>
    /// A unique key, used by Comicster to map local items to this
    /// catalog. A Uri (eg "http://example.com") is recommended.
    /// </summary>
    string Key { get; }

    /// <summary>
    /// A human-readable description of the catalog.
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Indicates whether the catalog is read-only or allows for Comicster to
    /// save items to it. "Save" methods should throw a NotSupportedException if
    /// this property is true.
    /// </summary>
    bool IsReadOnly { get; }

    IEnumerable<Character> FindCharacters(string searchTerm);
    IEnumerable<Creator> FindCreators(string searchTerm);
    IEnumerable<Publisher> FindPublishers(string searchTerm);
    IEnumerable<Trade> FindTrades(string searchTerm);
    IEnumerable<Title> FindTitles(string searchTerm);
    IEnumerable<Issue> FindIssues(string titleSearchTerm, string number);
    IEnumerable<Issue> FindTitleIssues(string titleId, string number);

    Publisher GetPublisher(string id);
    Creator GetCreator(string id);
    Character GetCharacter(string id);
    Trade GetTrade(string id);
    Title GetTitle(string id);
    Issue GetIssue(string id);

    void SavePublisher(Publisher publisher);
    void SaveCreator(Creator creator);
    void SaveCharacter(Character character);
    void SaveTrade(Trade trade);
    void SaveTitle(Title title);
    void SaveIssue(Issue issue);
}

There's a lot to implement if you want to support all the different catalog types, but if you throw a NotSupportedException then Comicster will handle it gracefully and inform the user.

Search Plug-Ins

Comicster File Plug-Ins

Comicster's Search page allows the user to search items in their collection using a plain text search, but also includes some "custom searches". You can add to the list of custom searches by implementing this interface, in Comicster.Searches:

public interface ISearch
{
    /// <summary>
    /// The text appearing in the list of custom searches for this search.
    /// </summary>
    string Text { get; }

    IEnumerable<Item> Execute(Collection collection);
}

This makes it trivially easy to add a custom search, because you're essentially just returning the results of a Linq query over the collection object.

Tool Plug-Ins

Comicster File Plug-Ins

The most versatile kind of plug-in is the custom tool, which can effectively do anything you want. It lives under the "Tools" menu, and Comicster discovers them by looking for classes that implement this interface from Comicster.Tools:

public interface ITool
{
    /// <summary>
    /// The text that will appear in the menu item for this tool.
    /// </summary>
    string Text { get; }

    bool CanExecute(IToolContext context);
    void Execute(IToolContext context);
}

Notice that the methods both accept a parameter of type IToolContext, which looks like this:

public interface IToolContext
{
    Collection Collection { get; }
    Item SelectedItem { get; }
    bool IsModified { get; }
    string FileName { get; }

    /// <summary>
    /// Sets the currently-active collection to a "modified" state so
    /// the user will be promted to save changes.
    /// </summary>
    void SetModified();
}
comicster mef
Posted by: Matt Hamilton
Last revised: 27 Nov, 2014 04:53 PM History

Trackbacks

Comments

06 Jun, 2011 06:11 PM @ version 3

Awesome write up.

Is there a way to have a ICollectionWriter without implementing ICollectionReader, for an export/save as only plug-in? I'm thinking about making a HTML or Excel exporter, but I don't want to implement a ICollectionReader. I'll also be using this when updating my code that generates the Android compatible db, but I don't want to support reading it right now, just writing to the format.

Also, as a side note, do you have any recommendations on plug-in assembly names? I was using Comicster.Tools.Name, but I think that might have potential filename conflicts if another developer creates a similar plug-in (e.g. Comicster.Tools.HtmlExport). Should we use our own namespaces (JamesEWelch.Comicster.HtmlExport) to prevent naming conflicts? Any recommendations?

06 Jun, 2011 11:04 PM @ version 3

Right now you have to implement reading if you want to implement writing, James. I didn't want "write only" file formats in the "File|Save" menu, because you're not saving it in any way that Comicster can reopen it later.

I think exporting is a useful feature, but it's one that could be done as a Tool plug-in really easily, so I think we're better off having a "Tools|Export to Foo..." menu item for that.

I'll update the post with naming recommendations for plug-in files. Thanks!

Toby
Toby
09 Jun, 2012 02:04 PM

Hello I recently installed the comicster app and the comicvine plug in.

I went to add my first comic by searching in the comicvine database and the default one and It cannot find a single thing on either of them...

Any ideas?

09 Jun, 2012 11:49 PM

Post on our Tender forum, Toby:

http://help.comicster.net

Make sure you include the things you've tried so far. I gather you've added a title and then clicked "Find in Catalog" after selecting it in the list?

No new comments are allowed on this post.