Navigation:  »No topics above this level«

Plugins development

Previous pageReturn to chapter overviewNext page

Plugins help to PNotes.NET to extend its functionality. Currently there are two types of plugins:

1.Plugins which allow to post notes' content on various social networks and receive posts (statuses, updates, twitts) from these networks with further inserting (replacing) their text into notes.
2.Plugins which allow to synchronize notes with one of available network storage providers.

 

Plugins are developed with MEF - the Managed Extensibility Framework,which is a composition layer for .NET that improves the flexibility, maintainability and testability of large applications. MEF can be used for third-party plugin extensibility, or it can bring the benefits of a loosely-coupled plugin-like architecture to regular applications (see more details about MEF here).

 

Each plugin represents .NET code library (DLL), written in any .NET language (C#, VB.NET etc) for .NET Framework version less or equal to 4 Client Profile - just because PNotes.NET requires .NET 4 Client Profile.

You are responsible for processing authentication against network server and other plugin's logic, such as posting messages and/or retrieving them etc.

Take a look at existing plugins' code here in order to learn how different plugins process authentication and other operations. You'll see that each one makes it by its own way.

Also you may notice that each plugin has two XML files: [pluginname]config.xml and [pluginname]localizations.xml. In first file you store any information related to authentication - keys, secrets etc. Of course, it's strongly recommended to store this data encrypted. Although it's up to you to choose the way you will store this data. In second file you may store all string messages your plugin may to show to user, in different languages, allowing to users add their own translations. This file is optional and you may prefer to do without it.

There are some rules though that are shared for all plugins and have to be implemented. Any plugin, for example, must implement IPostPlugin interface.

 

In this section we'll talk about social network plugin. Synchronization plugin is very similar instead of its inner logic.

Suppose you want to create a new plugin for, say, YourSpace network.

Let's start a new Class Library project in Visual Studio. Name it accordingly to the network you want to interact with, e.g. "pnyourspace" (this name must be unique among other plugins).

By default Class1.cs file is created in your project. Rename the "Class1" to something more sensible, e.g. "PNYourSpace". This is the class that will interact with PNotes.NET main window, so it's recommended to design it exactly as described below and leave all your own logic in another classes and forms.

First of all, add reference to PluginsCore.dll. It can be downloaded here: http://sourceforge.net/projects/pnotes/files/PNotes.NET/Plugins/PluginsCore_bin.zip/download

Other reference you require is System.ComponentModel.Composition.

There may be (and should be) other components to reference - such as libraries which works with OAuth 2.0 authentication protocol. You may write your own  library (just like I did for LinkedIn) or use existing one (Google is your best friend).

Once you have added all needed references, be sure to add the following usings:

 

using System;
using System.ComponentModel.Composition;
using System.Reflection;
using System.Windows.Forms;
using PluginsCore;

 

PluginsCore.dll contains all needed classes and interfaces.

While PNotes.NET main window implements IPluginsHost interface, which provides plugin with required properties from PNotes.NET, the plugin have to implement IPostPlugin interface. So add the following:

 

[Export(typeof(IPostPlugin))] // tells to MEF which type to export
public class PNYourSpace : IPostPlugin

 

IPostPlugin interface contains 1 method, 9 properties and 3 events. So you have to implement them as shown below:

 

public string Name
{
   get { return "YourSpace"; }
}

Here you return the name of your plugin, as it will be shown in PNotes.NET menus.

 

public void Init(IPluginsHost host)
{
   _Host = host;
   _MenuPostPartial = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
   _MenuPostFull = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
   _MenuGetPartial = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
   _MenuGetFull = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
}
 
private void menuClick(object sender, EventArgs e)
{
   var item = sender as ToolStripMenuItem;
   if (item == null) return;
   if (item.Equals(_MenuPostPartial))
   {
      RichTextBox edit = _Host.ActiveTextBox;
      if (edit != null)
      {
         var result = YourSpacePost.Post(edit.SelectedText, _Host.ActiveCulture);
         if (PostPerformed != null)
         {
            PostPerformed(this, new PostPerformedEventArgs(result));
         }
      }
   }
   else if (item.Equals(_MenuPostFull))
   {
      RichTextBox edit = _Host.ActiveTextBox;
      if (edit != null)
      {
         var result = YourSpacePost.Post(edit.Text, _Host.ActiveCulture);
         if (PostPerformed != null)
         {
            PostPerformed(this, new PostPerformedEventArgs(result));
         }
      }
   }
   else if (item.Equals(_MenuGetPartial))
   {
      var details = YourSpacePost.Get(_Host.LimitToGet, _Host.ActiveCulture);
      if (GotPostsPartial != null)
      {
         GotPostsPartial(this, new GotPostsEventArgs(details));
      }
   }
   else if (item.Equals(_MenuGetFull))
   {
      var details = YourSpacePost.Get(_Host.LimitToGet, _Host.ActiveCulture);
      if (GotPostsFull != null)
      {
         GotPostsFull(this, new GotPostsEventArgs(details));
      }
   }
}

Here you initialize your plugin, storing reference to host application main window and setting up plugin's menus, which will be inserted in PNotes.NET note's menus. Properties.Resources.myspc represents a small 16x16 image (most probably the social network logo) shown on menu item. The image should be stored as resource in your dll. In case you do not want to show any image, just send null instead.

menuClick is the name of procedure, which processes menus clicks.

Please, pay attention, that in case of _MenuPostPartial your plugin should post the selected text of _Host.ActiveTextBox, while in case of _MenuPostFull it should post the text of entire note.

_MenuGetPartial and _MenuGetFull both make the same operation and distinguish is performed in PNotes.NET accordingly to menu clicked.

YourSpacePost is your own class, where you implement your inner logic. It may have another methods' names than Post and Get, but return values of these methods have to be bool for Post (success or failure of posting message) and List<PostDetails> for Get. PostDetails class is defined in PluginsCore.dll.

_Host.ActiveTextBox is a text area of currently active PNotes.NET notes.

_Host.LimitToGet is a count of messages to retrieve from social network.

_Host.ActiveCulture is a culture currently selected in PNotes.NET as a culture of UI language. It is used for searching localized string in possible localization file.

 

public string Author
{
   get { return "your_name_goes_here"; }
}
 
public string Version
{
   get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
}

 

public string ProductName

{

   get 

   {

      var assembly = Assembly.GetExecutingAssembly();

       var customAttributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);

       return customAttributes.Length > 0 ? ((AssemblyProductAttribute)customAttributes[0]).Product : "";

   }

}

 

public string AdditionalInfo

{

    get { return "Uses YourSpace.NET library by J.J.Somebody (https://yourspacenet.com)"; }

}

Here you return your name as author of plugin, plugin's version, plugin's product name (required for update process) and additional plugin's information.

 

public ToolStripMenuItem MenuPostPartial
{
   get { return _MenuPostPartial; }
}
 
public ToolStripMenuItem MenuPostFull
{
   get { return _MenuPostFull; }
}
 
public ToolStripMenuItem MenuGetPartial
{
   get { return _MenuGetPartial; }
}
 
public ToolStripMenuItem MenuGetFull
{
   get { return _MenuGetFull; }
}

Here you return plugin's menus.

 

public event EventHandler<GotPostsEventArgs> GotPostsPartial;
public event EventHandler<GotPostsEventArgs> GotPostsFull;
public event EventHandler<PostPerformedEventArgs> PostPerformed;

Here you define plugin's events. GotPostsEventArgs and PostPerformedEventArgs classes are defined in PluginsCore.dll.

 

And here is the full example:

using System;
using System.ComponentModel.Composition;
using System.Reflection;
using System.Windows.Forms;
using PluginsCore;
 
namespace pnyourspace
{
    public class PNYourSpace : IPostPlugin
    {
        private IPluginsHost _Host;
        private ToolStripMenuItem _MenuPostPartial;
        private ToolStripMenuItem _MenuPostFull;
        private ToolStripMenuItem _MenuGetPartial;
        private ToolStripMenuItem _MenuGetFull;
 
        #region IPostPlugin Members
 
        public string Name
        {
            get { return "YourSpace"; }
        }
 
        public void Init(IPluginsHost host)
        {
            _Host = host;
            _MenuPostPartial = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
            _MenuPostFull = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
            _MenuGetPartial = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
            _MenuGetFull = new ToolStripMenuItem(Name, Properties.Resources.myspc, menuClick);
        }
 
        private void menuClick(object sender, EventArgs e)
        {
            var item = sender as ToolStripMenuItem;
            if (item == null) return;
            if (item.Equals(_MenuPostPartial))
            {
                RichTextBox edit = _Host.ActiveTextBox;
                if (edit != null)
                {
                    var result = YourSpacePost.Post(edit.SelectedText, _Host.ActiveCulture);
                    if (PostPerformed != null)
                    {
                        PostPerformed(this, new PostPerformedEventArgs(result));
                    }
                }
            }
            else if (item.Equals(_MenuPostFull))
            {
                RichTextBox edit = _Host.ActiveTextBox;
                if (edit != null)
                {
                    var result = YourSpacePost.Post(edit.Text, _Host.ActiveCulture);
                    if (PostPerformed != null)
                    {
                        PostPerformed(this, new PostPerformedEventArgs(result));
                    }
                }
            }
            else if (item.Equals(_MenuGetPartial))
            {
                var details = YourSpacePost.Get(_Host.LimitToGet, _Host.ActiveCulture);
                if (GotPostsPartial != null)
                {
                    GotPostsPartial(this, new GotPostsEventArgs(details));
                }
            }
            else if (item.Equals(_MenuGetFull))
            {
                var details = YourSpacePost.Get(_Host.LimitToGet, _Host.ActiveCulture);
                if (GotPostsFull != null)
                {
                    GotPostsFull(this, new GotPostsEventArgs(details));
                }
            }
        }
 
        public string Author
        {
            get { return "your_name_goes_here"; }
        }
 
        public string Version
        {
            get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
        }
       

         public string ProductName

         {

            get 

            {

               var assembly = Assembly.GetExecutingAssembly();

                var customAttributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);

                return customAttributes.Length > 0 ? ((AssemblyProductAttribute)customAttributes[0]).Product : "";

            }

        }

 
        public ToolStripMenuItem MenuPostPartial
        {
            get { return _MenuPostPartial; }
        }
 
        public ToolStripMenuItem MenuPostFull
        {
            get { return _MenuPostFull; }
        }
 
        public ToolStripMenuItem MenuGetPartial
        {
            get { return _MenuGetPartial; }
        }
 
        public ToolStripMenuItem MenuGetFull
        {
            get { return _MenuGetFull; }
        }
 
        public event EventHandler<GotPostsEventArgs> GotPostsPartial;
        public event EventHandler<GotPostsEventArgs> GotPostsFull;
        public event EventHandler<PostPerformedEventArgs> PostPerformed;
 
        #endregion
    }
}