Book Image

Redmine Plugin Extension and Development

By : Alex Bevilacqua
Book Image

Redmine Plugin Extension and Development

By: Alex Bevilacqua

Overview of this book

Table of Contents (16 chapters)
Redmine Plugin Extension and Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Diving into the initialization file


Every Redmine plugin requires an initialization file (init.rb) to be included in order for the plugin to be registered with Redmine upon startup.

A stripped down version of the initialization file we'll be working on is included in the following snippet to highlight some of the attributes and helpers that are available:

Redmine::Plugin.register :redmine_knowledgebase do
  name        'Knowledgebase'
  author      'Alex Bevilacqua'
  author_url  'http://www.alexbevi.com'
  description 'a knowledgebase plugin for Redmine'
  url         'https://github.com/alexbevi/redmine_knowledgebase'
  version     ' 3.0.0'

  requires_redmine :version_or_higher => '2.0.0'
  
  settings :default => {
    :sort_category_tree => true,
    :show_category_totals => true,
    :summary_limit => 5,
    :disable_article_summaries => false
  }, :partial => 'settings/knowledgebase_settings'

  project_module :knowledgebase do
    permission :view_articles, {
      :knowledgebase => :index,
      :articles      => [:show, :tagged],
      :categories    => [:index, :show]
    }
    permission :create_articles, {
      :knowledgebase => :index,
      :articles      => [:show, :tagged, :new, :create, :preview],
      :categories    => [:index, :show]
    }
    # ...
  end
end

This plugin registration block contains field definitions that are used to identify the plugin to Redmine.

Note

As of Redmine 2.3.3, based on the identifier with which the plugin was registered (:redmine_knowledebase in this case), the plugin would have to reside in /path/to/redmine/plugins/redmine_knowledgebase in order to be detected properly. Note that this can be overridden using a directory attribute in future versions of Redmine, as per http://www.redmine.org/issues/13927.

Plugin attributes

The values of these fields are used to either identify the plugin to the administrator when they visit the plugin list at http://localhost:3000/admin/plugins of their Redmine deployment, or to provide some assignment or initialization functionality.

Tip

Ruby on Rails application default to port 3000 when run locally. As this is standard, we'll be using http://localhost:3000 as the base URL for all Redmine links.

The attributes that can be provided to the Redmine::Plugin.register block are as follows:

  • name: This is the full name of the plugin.

  • description: This gives a brief description of what the plugin does.

  • url: This is the website of the plugin itself. This is generally the online repository URL (GitHub, Bitbucket, Google Code, and so on), or plugin website (if available or applicable).

  • author: This holds the name(s) of the author(s) of the plugin.

  • author_url: This is generally the link to either the author(s)' e-mail addresses or blogs.

  • version: This is the internal version number of the plugin. Though not required, it is a good practice to use Semantic Versioning (see http://semver.org for more information), as Redmine follows a similar (though not official) numbering scheme.

  • settings: This field is used to define and set the default values of internal plugin settings and link to a view partial, which system administrators can use to set plugin configuration values.

    settings :default => {
        :sort_category_tree => true,
      }, :partial => 'settings/knowledgebase_settings' 

The preceding example lets our plugin know that we will be providing a configuration partial, as well as initializing a custom settings value of sort_category_tree to true.

As Redmine plugins follow the standard Ruby on Rails application hierarchy, the implied location of our settings partial would be /path/to/redmine/plugins/redmine_knowledgebase/app/views/settings/_knowledgebase_settings.html.erb.

Settings management will be covered in more detail in Chapter 7, Managing Plugin Settings.

Initialization checks

Redmine provides a number of helper functions that can be used to assist plugin authors ensuring compatibility with different versions of Redmine, as well as other plugins.

Checking for a specific Redmine version

The version or versions of Redmine that a plugin is compatible with can be specified within the plugin initialization file using the requires_redmine helper.

This helper allows the plugin author to alert Redmine system administrators that the plugin is not intended to run with the administrator's version of Redmine. Some examples of the types of version checks that can be performed are as follows:

  • Exact match

    requires_redmine "2.3.3"
    requires_redmine :version => "2.3.3"
  • Exact match of more than one version

    requires_redmine :version => ["2.2.0", "2.3.0"]
  • Match a specific version and revision

    requires_redmine "2.3"
    requires_redmine :version => "2.3"
  • Minimum version or higher

    requires_redmine :version_or_higher => "2.3.3"
  • Range of versions

    requires_redmine :version => "2.2.0".."2.3.0"
    requires_redmine :version => "2.2".."2.3"

Ensuring the existence of other plugins

Similar to the requires_redmine helper, the requires_redmine_plugin function is used to limit the successful deployment of our plugin based on the availability of another Redmine plugin.

The following examples are based on a plugin named :sample_plugin being included for availability and version checks:

  • Exact match

    requires_redmine_plugin :sample_plugin :version => "1.0.0"
    requires_redmine_plugin :sample_plugin "1.0.0"
  • Minimum version or higher

    requires_redmine_plugin :sample_plugin :version_or_higher => "1.0.0"
  • Range of versions

    requires_redmine_plugin :sample_plugin :version => ["0.1.0", "0.2.0"]

Extending core Redmine features

Now that we've initialized our plugin with some basic details and requirements, we can start integrating directly with Redmine.

A number of helper methods are available to plugin authors, which facilitate this integration with core components, such as menus and permissions.

Working with Redmine menus

The menu helper, which is also aliased to add_menu_item, allows us to inject custom entries into various content areas of Redmine. The syntax for adding a menu item is:

menu(menu, item, url, options = {})

The options hash can accept any number of the following parameters:

  • :param: This is the parameter key that will be used as the project ID (the default is :id).

  • :if: This is a proc that prevents the menu from rendering unless it is evaluated to true.

  • :caption: This is the menu caption (label), which can be a localized symbol, proc, or string.

  • :before or :after: This is used to position the menu entry relative to an existing entry. For example, :after => :activity, or :before => :issues.

  • :first or :last: If either of these options is set to true, the menu item will be placed at the absolute beginning or end of the target menu.

  • :html: This is a hash of HTML options that will be passed to the link_to instance that is used to render the menu item.

Redmine also provides a function we can use in our plugin to remove menu items, the syntax for which is:

delete_menu_item(menu, item)

The following example injects an entry into the project menu. Note that although you've added a new menu item, it may still not be available to all users due to insufficient permissions.

menu :project_menu, 
  :articles, 
  { :controller => 'articles', :action => 'index' }, 
  :caption => :knowledgebase_title, 
  :after => :activity, 
  :param => :project_id

The other valid targets for the menu are admin_menu, top_menu, account_menu, and application_menu.

The admin_menu target is used to add custom entries to the Administration menu, which is available at http://localhost:3000/admin, and can insert custom entries between the Settings and Plugins menu items.

Initializing named permissions

The permission helper is used to define a named permission for the given actions. The syntax for this helper is:

permission(name, actions, options = {})

The actions argument is a hash with controllers as keys and actions as values (a single value or an array):

  permission :destroy_contacts, { :contacts => :destroy }
  permission :view_contacts,    { :contacts => [:index, :show] }

The valid options are as follows:

  • :public: This changes the permission to public if set to true (implicitly given to any user)

  • :require: This can be set to either :loggedin or :member, and is used to further restrict the types of users the permission can be applied to

  • :read: This is set to true so that the permission is still granted on closed projects

Permissions will be covered in more detail in Chapter 3, Permissions and Security.

Project module availability

If our plugin will be adding functionality at the project level (as opposed to globally) within Redmine, we'll need to define a project_module block.

A project module is effectively a functional area within Redmine whose data belongs to a specific project, or whose scope can be limited to a project. Examples of project modules are issues, documents, wikis, or time tracking features.

Permissions defined within the project_module block will be bound to the module, as follows:

project_module :knowledgebase do
  permission :view_articles, {
    :knowledgebase => :index,
    :articles      => [:show, :tagged],
    :categories    => [:index, :show]
  }
  permission :comment_and_rate_articles, {
    :knowledgebase => :index,
    :articles      => [:show, :tagged, :rate, :comment, :add_comment],
    :categories    => [:index, :show]
  }
  # ...
end

Adding custom events to the activity stream

Activity providers are essentially models that have been defined to provide events to the activity fetcher. Once a model has registered an activity provider, activities will be mixed into a project's activity stream.

A model can provide several activity event types, which are registered by passing event types and optional class names to the activity_provider helper plugin:

  activity_provider :news
  activity_provider :scrums, :class_name => 'Meeting'
  activity_provider :issues, :class_name => ['Issue', 'Journal']

Using the activity_provider helper simply indicates that there are activity providers registered. The syntax for the helper functions is:

activity_provider(*args)

The helper simply wraps Redmine::Activity.register, which is available at /path/to/redmine/lib/redmine/activity.rb.

A matching acts_as_activity_provider entity must be initialized at the model level in order to actually utilize this functionality.

We will cover activity provider configuration in more detail in Chapter 6, Interacting with the Activity Stream.

Registering custom text formatting macros

Our knowledgebase plugin will be used to create articles, which we may want to reference in other Redmine content areas.

For example, if we want to register the kb#1 macro to link to a knowledgebase article with an ID value of 1, we would first need to register the macro with a Redmine::WikiFormatting::Macros.register block similar to the following:

Redmine::WikiFormatting::Macros.register do
  desc "Knowledge base Article link Macro, using the kb# format"
  macro :kb do |obj, args|
    args, options = extract_macro_options(args, :parent)
    raise 'One argument expected' if args.size != 1
    article = KbArticle.find(args.first)
    link_to_article(article)
  end
end

We could now include the text kb#1 in an issue, document, wiki, or anywhere else where Redmine formats text (see http://www.redmine.org/projects/redmine/wiki/RedmineTextFormatting for existing formatting options) and it would render as a link back to our knowledgebase article.