Interacting with the Activity Stream - Redmine Plugin Extension and Development (2014)

Redmine Plugin Extension and Development (2014)

Chapter 6. Interacting with the Activity Stream

One of the most useful features of Redmine is the ability to provide a generic, timestamp-sorted listing of the happenings within a project (or all projects) using the activity stream.

Whether we're looking for changesets, issue updates, news, a document of forum submissions, or any other Redmine project module update, the activity stream will provide a summarized representation of any content changes.

For our plugin to fully integrate into a Redmine project as a project module, any update we create should also be reflected within the project's activity stream.

This chapter will introduce the activity stream and how it can be leveraged by plugin authors in order to provide activity summaries in line with other Redmine activities.

We will cover the following topics in this chapter:

· A summary of Redmine's activity listing subsystem

· An overview of the acts_as_activity_provider internal plugin

· How the acts_as_event plugin applies to acts_as_activity_provider

· How to customize activity stream entries through acts_as_event

Overview of the activity stream

A project's activity stream is directly accessible by navigating to the Activity tab. Once selected, all available activities are summarized, including present activities.

If we wish to see all activities from all available projects, either go to Projects in the application bar and then select Overall Activity or navigate directly to http://localhost:3000/activity.

Any model within Redmine that implements the acts_as_activity_provider plugin can be displayed in the following listing:

Overview of the activity stream

The preceding screenshot is taken from the official bug tracker for the Ruby language (https://bugs.ruby-lang.org/activity).

The screenshot illustrates the two main components of an activity stream:

· The activity filter list

· The activity stream

The filter list allows users to select the project modules that are available in the stream, assuming their activity providers are defined.

The stream is a list sorted in reverse chronology and comprises all the items that have been selected from the filter list and occur within the range that has been defined by the system administrator.

Note

For system administrators, the activity stream limit can be set in http://localhost:3000/settings, in the setting entry for Days displayed on project activity under the General tab.

Preparing our model

We're going to adapt our KbArticle model so that new articles will be listed in the activity stream of any project with a knowledgebase.

In Chapter 5, Making Models Searchable, we introduced the acts_as_event plugin as a prerequisite to use the acts_as_searchable plugin; it also serves as a prerequisite for acts_as_activity_provider.

If we implement acts_as_activity_provider without acts_as_event and try to load an activity stream, Redmine will crash with a NoMethodError exception:

NoMethodError (undefined method `event_datetime' for #<KbArticle:0x000000042b9428>)

The example we provided in the previous chapter is being cited here for continuity:

class KbArticle < ActiveRecord::Base

# ...

acts_as_event :datetime => :updated_at,

:description => :summary,

:title => Proc.new { |o| "#{l(:label_title_articles)} ##{o.id} - #{o.title}" },

:url => Proc.new { |o| { :controller => 'articles',

:action => 'show',

:id => o.id,

:project_id => o.project }

}

# ...

end

Note that if your model uses more than one internal Redmine plugin that relies on acts_as_event, you don't have to implement acts_as_event more than once.

Registering our model

Our model is now capable of integrating with the Redmine activity stream; however, Redmine is still unaware of our model in this context.

In order to have our model's events actually represented in the activity stream, our plugin initialization file needs to be updated.

Redmine::Activity.register :kb_articles

The preceding entry needs to be added to our init.rb file after the Redmine::Plugin.register block.

Now that we have registered our model with Redmine, the implementation of acts_as_activity_provider will produce results in a project's activity stream.

Configuring an activity provider

For a model to be designated as an activity provider, we'll need to implement the acts_as_activity_provider plugin that comes with Redmine.

The method's signature for this plugin is as follows:

def acts_as_activity_provider(options = {})

If we're looking to dive directly into the source code for the plugin, it is available as part of our Redmine installation at /path/to/redmine/lib/plugins/acts_as_activity_provider/lib/acts_as_activity_provider.rb.

The acts_as_activity_provider plugin is another class extension plugin and requires us to call the acts_as_activity_provider method within our model's class definition along with some parameters as follows:

class KbArticle < ActiveRecord::Base

# ...

acts_as_activity_provider :find_options => {:include => :project},

:author_key => :author_id,

:type => 'kb_articles',

:timestamp => :updated_at

# ...

end

The acts_as_activity_provider method takes a hash as a parameter. This options hash accepts a number of potential keys, although none of them is strictly required due to the default values being available for most. Let's have a look at them:

· :type: Using this, multiple event types can be represented in an activity stream, and therefore, unique identifiers are required to keep these events separated. The default value of this option is the model's class name, which is underscored and pluralized. For example, our model name is KbArticle, so the default name will be kb_articles.

· :permission: This is used if a custom permission has been defined for a user to view our model's content in an activity stream. If a permission value has been defined as nil, the default value of :view_project will be used instead. Even though a default value is available, it is a good practice to provide a permission named by us as it better isolates our plugin from core Redmine. For more in-depth coverage of permissions, see Chapter 3, Permissions and Security.

· :timestamp: This is the datetime field that is used to establish a sort order for the model. If no value is provided, a default value of the model's created_on field is used.

· :author_key: This is a symbol that identifies the field within our model that contains an ID for the user that created a new record. This value needs to map to a valid Redmine user.

· :find_options: This is used if additional filtration details need to be provided in order to limit the activity stream results. The values provided to the :find_options hash should be standard ActiveRecord query options. For example, find_options in our previous sample implementation contains :find_options => { :include => :project }, which allows us to name the project association that will be loaded alongside our model.

Now that we have our activity provider defined and implemented, the Activity tab of any project in which we've enabled the knowledgebase functionality will contain knowledgebase articles in the stream.

Customizing activity entries

The acts_as_event plugin mentioned previously is being used to provide a consistent representation of our data across multiple models by defining common elements.

The ability to provide Proc as a parameter in most fields (see Chapter 5, Making Models Searchable) means that we can include executable code within our declaration.

In the sample provided earlier in this chapter, we listed article titles as a combination of their ID value as well as their title:

acts_as_event :title => Proc.new { |o| "#{l(:label_title_articles)} ##{o.id} - #{o.title}" }

Customizing activity entries

We're already using a procedure in order to build the link title, so we can take this even further and append additional information.

In the following example, we'll attach the article tag list to the article title:

acts_as_event :title => Proc.new { |o| tags = (o.tag_list.blank?) ? nil : "[#{o.tag_list.join(', ')}]"; "#{l(:label_title_articles)} ##{o.id} - #{o.title} #{tags}" }

Customizing activity entries

The link to the article in the activity stream now contains the individual tags following the article title.

Note that if we change how an element in acts_as_event is being formatted, this change will also be reflected in all search results.

Note

Unlike acts_as_activity_provider, the acts_as_event plugin can only be included once per model.

Summary

The activity stream is an extremely useful feature of Redmine and is generally the starting point for a lot of users when they enter the system.

Having our plugins register with the activity stream provides greater value to end users as they can quickly see what has been happening within our plugin from the same screen they would be following up with other plugins and core system events.

In this chapter, we learned how to include our plugin in a project's activity stream as well as how to tweak the formatting of the results.

In the next chapter, we'll explore plugin configuration management.