eXo Platform 3.5

## Chapter 5. Developer reference

This chapter supplies you with the basic knowledge about overridden components, internal APIs implemented in eXo Knowledge. Also, you will know how to set up a default template for the FAQ portlet, and how to create a sample action extension in Wiki.

In this chapter, there are the following main topics:

- Extension points

- Internal API

- FAQ Template Configuration

- Extend actions over a wiki page from external jars

## Extension points

There are some extension points in eXo Knowledge, so that you can control how these components work by implementing or extending default implementations, then reconfigure these new components in the configuration.xml file.

### ForumEventLifeCycle

#### Overview

`ForumEventLifeCycle` enables you to listen to the lifecycle of a forum. By implementing ForumEventLifeCycle, you can be notified of new posts and replies, categories and topics. This installation will be injected when the data flow is called to save data.

#### Configuration plug-in

You can find the configuration file of this component at: ext/social-integration/src/main/resources/conf/portal/configuration.xml.

For example, to add a Forum to a space of the Social application and keep new activities of Forum (such as new posts and topics) updated to the activities of space, do as follows:

    <external-component-plugins>

      <target-component>org.exoplatform.forum.service.ForumService</target-component>

      <component-plugin>

        <name>ForumEventListener</name>

        <set-method>addListenerPlugin</set-method>

        <type>org.exoplatform.ks.ext.impl.ForumSpaceActivityPublisher</type>

      </component-plugin>

    </external-component-plugins>

#### Tutorial

To use ForumEventLifeCycle class, do the following steps:

1. Create a new class that extends ForumEventListener.

For example: class ABCActivityPublisher

    public class ABCActivityPublisher extends ForumEventListener {

    ..............

    }

2. Override fuctions in this created class. In each function, you can write anythings to meet your needs.

    public class ABCActivityPublisher extends ForumEventListener {

      public void saveCategory(Category category){

        ....

      }

      public void saveForum(Forum forum){

        ....

      }

      public void addTopic(Topic topic, String categoryId, String forumId)){

        ....

      }

      public void updateTopic(Topic topic, String categoryId, String forumId){

        ....

      }

      public void addPost(Post post, String categoryId, String forumId, String topicId){

        ....

      }

      public void updatePost(Post post, String categoryId, String forumId, String topicId){

        ....

      }

    }

- The function saveCategory is called when a category is added and/or edited.

- The function saveForum is called when a forum is added and/or edited.

- The function addTopic is called when a topic is added.

- The function updateTopic is called when a topic is updated.

- The function addPost is called when a post is added.

- The function updatePost is called when a post is updated.

3. Add a new configuration to the configuration.xml file with the type that is the class created in the Step 1.

    <external-component-plugins>

      <target-component>org.exoplatform.forum.service.ForumService</target-component>

      <component-plugin>

        <name>ForumEventListener</name>

        <set-method>addListenerPlugin</set-method>

        <type>{package}.{class name}</type>

      <!-- example

      <type>org.exoplatform.ks.ext.impl.ABCActivityPublisher</type>

      -->

      </component-plugin>

    </external-component-plugins>

### AnswerEventLifeCycle

#### Overview

`AnswerEventLifeCycle` installs event updates for the Answers data that is injected while saving answers, saving questions or posting comments.

#### Configuration plug-in

You can find the configuration file of this component at:_ext/social-integration/src/main/resources/conf/portal/configuration.xml_.

For example, to add Answers to a space of the Social application and keep new activities of Answers updated to the activities of space, do as follows:

    <external-component-plugins>

      <target-component>org.exoplatform.faq.service.FAQService</target-component>

      <component-plugin>

        <name>AnswerEventListener</name>

        <set-method>addListenerPlugin</set-method>

        <type>org.exoplatform.ks.ext.impl.AnswersSpaceActivityPublisher</type>

      </component-plugin>

    </external-component-plugins>

In which, `AnswersSpaceActivityPublisher` is the class to implement `ForumEventLifeCycle`.

#### Tutorial

To use the AnswerEventLifeCycle class, do the following steps:

1. Create a new class that extends AnswerEventListener.

For example: ABCActivityPublisher

    public class ABCActivityPublisher extends AnswerEventListener {

      ....

    }

2. Override fuctions in this created class. In each function, you can write anything to meet your needs.

    public class ABCActivityPublisher extends AnswerEventListener {

      public void saveQuestion(Question question, boolean isNew){

        ....

      }

      public void saveAnswer(String questionId, Answer answer, boolean isNew){

        ....

      }

      public void saveAnswer(String questionId, Answer[] answers, boolean isNew){

        ....

      }

      public void saveComment(String questionId, Comment comment, boolean isNew){

        ....

      }

    }

- The function saveQuestion is called when a question is added and/or edited.

- The function saveAnswer is called when an answer is added and/or edited.

- The function saveAnswer is called when answers are added and/or edited.

- The function saveComment is called when a comment is added and/or edited.

3. Add a new configuration to the configuration.xml file with the type that is the class created in the Step 1.

    <external-component-plugins>

      <target-component>org.exoplatform.faq.service.FAQService</target-component>

      <component-plugin>

        <name>AnswerEventListener</name>

        <set-method>addListenerPlugin</set-method>

        <type>{package}.{class name}</type>

      <!-- example

      <type>org.exoplatform.ks.ext.impl.ABCActivityPublisher</type>

      -->

      </component-plugin>

    </external-component-plugins>

### BBCodeRenderer

`BBCodeRenderer` is used in the core of eXo Knowledge to render BBCodes. In which, the data input is text, containing BBCode tags. The data output will be BBCode tags which have been encrypted into HTML tags.

You can find the configuration file of this component at: extension/webapp/src/main/webapp/WEB-INF/ks-extension/ks/forum/bbcodes-configuration.xml.

For example, to register BBCodeRenderer, do as follows:

    <external-component-plugins>

      <target-component>org.exoplatform.ks.rendering.MarkupRenderingService</target-component>

      <component-plugin>

        <name>BBCodeRenderer</name>

        <set-method>registerRenderer</set-method>

        <type>org.exoplatform.ks.rendering.spi.RendererPlugin</type>

        <description>BBCode renderer</description>

        <init-params>

          <object-param>

            <name>renderer</name>

            <description>Extended BBCodeRenderer</description>

            <object type="org.exoplatform.ks.bbcode.core.BBCodeRenderer">

              <field name="bbCodeProvider">

                <object type="org.exoplatform.ks.bbcode.core.ExtendedBBCodeProvider"/>

              </field>

            </object>

          </object-param>

        </init-params>

      </component-plugin>

    </external-component-plugins>

In which, `ExtendedBBCodeProvider` is the class to implement `BBCodeProvider`.

## Internal API

Applications of eXo Knowledge, such as Forum, Answers and Polls, use REST services to communicate with the server. This section describes REST Services of internal APIs.

### Forum application

The Forum application of eXo Knowledge uses the ForumWebservice to provide all APIs to work with Forum, such as filtering IPs, viewing RSS, and more.

Service name | Service URL | Description | Location
--- | --- | --- | ---
ForumWebservice | $portalName/ $restcontextname/ ks/forum/ | Call extended services of the Forum application. | * Maven groupId: org.exoplatform.ks * ArtifactId: exo.ks.web.webservice

- APIs Usage

Use the following APIs to build all the functions of the Forum application:

Name | Description | Service URL endpoint | Parameters | Expected values
--- | --- | --- | --- | ---
getMessage | Get all new posts with the number based on the maxcount value. | $portalName/ $restcontextname/ ks/forum/getmessage/ | String maxcount | integer
filterIps | Ban IPs with no access to Forum. | $portalName/ $restcontextname/ ks/forum/filter/ | String str | String in the IP format.
filterIpBanForum | Ban IPs with no access to a specific Forum. | $portalName/ $restcontextname/ ks/forum/ filterIpBanforum/ | String forumId String str | string in the IP format, string in the Forum format
filterTagNameForum | Do the quick search for tags that returns the list of tags. | $portalName/ $restcontextname/ ks/forum/ filterTagNameForum/ | String str String userAndTopicId | Tag name id of user and topic
viewrss | Process the request by users when they want to view RSS. | $portalName/ $restcontextname/ ks/forum/rss/ | String resourceid | category id forum id topic id
userrss | Process the request by users when they want to view their own collected RSS. | $portalName /$restcontextname/ ks/forum/rss/user/ | String resourceid | username id

### Answers application

The Answers application of eXo Knowledge uses the FAQWebservice to view RSS.

Service name | Service URL | Description | Location
--- | --- | --- | ---
FAQWebservice | $portalName/ $restcontextname/ ks/faq | Call the extended services of the Answers application. | * Maven groupId: org.exoplatform.ks * ArtifactId: exo.ks.eXoApplication. faq.service

- APIs Usage

Use the following APIs to build all the functions of Answers application:

Name | Description | Service URL endpoint | Parameters | Expected values
--- | --- | --- | --- | ---
viewrss | View RSS | $portalName/ $restcontextname/ ks/faq/rss/ | String resourceid | category id, question id

### Polls application

The Polls application of eXo Knowledge uses the PollWebservice to create and interact with the Polls gadget.

Service name | Service URL | Description | Location
--- | --- | --- | ---
PollWebservice | $portalName/ $restcontextname/ private/ks/poll | Call extended services of the Polls application. | * Maven groupId: org.exoplatform.ks * ArtifactId: exo.ks.eXoApplication. poll.service

- APIs Usage

Use the following APIs to build all functions of the Answers application:

Name | Description | Service URL endpoint | Parameters | Expected values
--- | --- | --- | --- | ---
viewPoll | Return data of the Polls system | $portalName/ $restcontextname/ private/ks/poll/ viewpoll/ | String pollId | id of poll
votePoll | Update data for the Polls with the returned id | $portalName/ $restcontextname/ private/ks/poll /votepoll/ | String pollId String indexVote | id of poll integer

## FAQ Template Configuration

### Configuration plug-in

Configuration plug-in is used to automatically set up a default template for the FAQ portlet. When the FAQ service starts, it will get values which are returned from the TemplatePlugin component to initialize the template for the FAQ portlet.

The template configuration plug-in is configured in the templates-configuration.xml file.

In details:

At runtime of the FAQ Service, FAQService component is called, then templates-configuration.xml file is executed. The component-plugin named addTemplatePlugin will be referred to org.exoplatform.faq.service.TemplatePlugin- to execute some objects and create default data for the Forum application.

    <external-component-plugins>

      <target-component>org.exoplatform.faq.service.FAQService</target-component>

      <component-plugin>

        <name>faq.default.template</name>

        <set-method>addTemplatePlugin</set-method>

        <type>org.exoplatform.faq.service.TemplatePlugin</type>

        <init-params>

          <value-param>

            <name>viewerTemplate</name>

            <value>war:/ks-extension/ks/faq/templates/FAQViewerPortlet.gtmpl</value>

          </value-param>

        </init-params>

      </component-plugin>

    </external-component-plugins>

The properties of template configuration plug-in are defined in the init-params tag as follows:

    <init-params>

      <value-param>

        <name>viewerTemplate</name>

        <value>war:/ks-extension/ks/faq/templates/FAQViewerPortlet.gtmpl</value>

      </value-param>

    </init-params>

Name | Description | Value
viewerTemplate | Path of file template. | war:/ks-extension/ks/faq/templates/ FAQViewerPortlet.gtmpl

### How to change look and feel

You can change the template FAQ viewer in one of the following two ways:

- By using Plug-in.

- By using the edit mode to edit template.

#### Plug-in

1. Create a file named FAQViewerPortlet.gtmpl . The content of the file is the template of the FAQ viewer.

2. Copy this file and paste into ks-extension/WEB-INF/ks-extension/ks/faq/templates/ that is in the webapps folder of the server (tomcat, jboss).

When the server runs, FAQViewerPortlet.gtmpl will initialize the template of the FAQ viewer.

#### Edit Mode

1. Run the server and open the FAQ Portlet.

2. Go to edit mode and open the Edit Template tab.

3. Edit the content of text-area-input and click Save.

### API provided by the UIComponent (UIViewer.java)

- UIViewer is the child of the component UIFAQPortlet. It shows the main content of FAQ portlet.

- List of APIs:

Function name | Param | Return | Description
--- | --- | --- | ---
getCategoryInfo | Empty | CategoryInfo object | Get the object CategoryInfo
arrangeList | `(List< String > list)`: List of path | A new list is arranged | Arrange a list of path.
render | `(String)`: The content of answers or comments | A new string is converted by function render | Render the content of answers or comments.

- The CategoryInfo class:

    ...

    private String id;

    private String path;

    private String name;

    private List<String> pathName;

    private List<QuestionInfo> questionInfos = new ArrayList<QuestionInfo>();

    private List<SubCategoryInfo> subCateInfos = new ArrayList<SubCategoryInfo>();

    ...

Param | Type | Description
--- | --- | ---
id | `String` | The jcr node name of the category node.
path | `String` | The jcr node path of the category node.
name | `String` | The name of the category.
pathName | `List<String>` | The path to the category includes a list of category names.
questionInfos | `List<QuestionInfo>` | The list of QuestionInfo object.
subCateInfos | `List<SubCategoryInfo>` | The list of SubCategoryInfo object.

- The QuestionInfo class:

    ...

    private String id;

    private String question;

    private String detail;

    private List<String> answers = new ArrayList<String>();

    ...

Param | Type | Description
--- | --- | ---
id | `String` | The jcr node name of the question node.
question | `String` | The content of the question.
details | `String` | Details of the question.
answers | `List<String>` | The list of answers for the question.

- The SubCategoryInfo class:

The params of this class are the same as those of the CategoryInfo class. See here for more information.

## Extend actions over a wiki page from external jars

This section describes how to create a sample action extension in Wiki.

### Overview

The toolbar in Wiki is built basing on the UI Extension Framework, so you can add your own actions packaged in external jars to it.

### How to add action extension to the Wiki toolbar

After reading this section, you can add your own actions to the Wiki toolbar. In the following example, you are going to add a new action on the Wiki toolbar to view the Wiki markup of a page.

#### Create a new project for action extension

Create a Maven project which has the following directory structure:

Navigating in the project's folder, you will see the following structure:

- pom.xml: the project's POM file.

- src/main/java/.../ViewSourceActionComponent.java: a simple action supporting user to view the wiki markup of a page.

- src/main/resources/conf/portal/configuration.xml: the configuration file to register your actions with the org.exoplatform.webui.ext.UIExtensionManager service.

Here is the content of the pom.xml file:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.acme</groupId>
      <artifactId>example</artifactId>
      <version>1.0-SNAPSHOT</version>
      <name>eXo Wiki action - Example</name>
      <description>eXo Wiki action - Example</description>
      <dependencies>
        <dependency>
          <groupId>org.exoplatform.portal</groupId>
          <artifactId>exo.portal.webui.core</artifactId>
          <version>3.2.0-PLF-SNAPSHOT</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.exoplatform.commons</groupId>
          <artifactId>exo.platform.commons.webui.ext</artifactId>
          <version>1.1.3-SNAPSHOT</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.exoplatform.ks</groupId>
          <artifactId>exo.ks.eXoApplication.wiki.service</artifactId>
          <version>2.2.4-SNAPSHOT</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.exoplatform.ks</groupId>
          <artifactId>exo.ks.eXoApplication.wiki.webapp</artifactId>
          <version>2.2.4-SNAPSHOT</version>
          <type>jar</type>
          <scope>provided</scope>
        </dependency>
      </dependencies>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <source>1.5</source>
              <target>1.5</target>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>

#### Create new actions and their corresponding listeners

Edit the ViewSourceActionComponent class as below:

    package com.acme;

    import java.util.Arrays;
    import java.util.List;

    import org.exoplatform.webui.config.annotation.ComponentConfig;
    import org.exoplatform.webui.config.annotation.EventConfig;
    import org.exoplatform.webui.event.Event;
    import org.exoplatform.webui.ext.filter.UIExtensionFilter;
    import org.exoplatform.webui.ext.filter.UIExtensionFilters;
    import org.exoplatform.wiki.commons.Utils;
    import org.exoplatform.wiki.mow.core.api.wiki.PageImpl;
    import org.exoplatform.wiki.webui.UIWikiContentDisplay;
    import org.exoplatform.wiki.webui.UIWikiPageContentArea;
    import org.exoplatform.wiki.webui.UIWikiPortlet;
    import org.exoplatform.wiki.webui.control.action.core.AbstractEventActionComponent;
    import org.exoplatform.wiki.webui.control.filter.IsViewModeFilter;
    import org.exoplatform.wiki.webui.control.listener.MoreContainerActionListener;

    @ComponentConfig (
      template = "app:/templates/wiki/webui/control/action/AbstractActionComponent.gtmpl",
      events = {
            @EventConfig(listeners = ViewSourceActionComponent.ViewSourceActionListener.class)
        }
    )
    public class ViewSourceActionComponent extends AbstractEventActionComponent {

      public static final String                   ACTION  = "ViewSource";

      private static final List<UIExtensionFilter> FILTERS = Arrays.asList(new UIExtensionFilter[] { new IsViewModeFilter() });

      @UIExtensionFilters
      public List<UIExtensionFilter> getFilters() {
        return FILTERS;
      }

      @Override
      public String getActionName() {
        return ACTION;
      }

      @Override
      public boolean isAnchor() {
        return false;
      }

      public static class ViewSourceActionListener extends MoreContainerActionListener<ViewSourceActionComponent> {
        @Override
        protected void processEvent(Event<ViewSourceActionComponent> event) throws Exception {
          UIWikiPortlet wikiPortlet = event.getSource().getAncestorOfType(UIWikiPortlet.class);
          UIWikiContentDisplay contentDisplay = wikiPortlet.findFirstComponentOfType(UIWikiPageContentArea.class)
                                                           .getChildById(UIWikiPageContentArea.VIEW_DISPLAY);
          PageImpl wikipage = (PageImpl) Utils.getCurrentWikiPage();
          contentDisplay.setHtmlOutput(wikipage.getContent().getText());
          event.getRequestContext().addUIComponentToUpdateByAjax(contentDisplay);
        }

      }

    }

#### Register new actions with UIExtensionManager

Edit the configuration.xml file as below:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
      xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">

      <external-component-plugins>
        <target-component>org.exoplatform.webui.ext.UIExtensionManager</target-component>
        <component-plugin>
          <name>add.action</name>
          <set-method>registerUIExtensionPlugin</set-method>
          <type>org.exoplatform.webui.ext.UIExtensionPlugin</type>
          <init-params>
            <object-param>
              <name>ViewSource</name>
              <object type="org.exoplatform.webui.ext.UIExtension">
                <field name="type"><string>org.exoplatform.wiki.webui.control.MoreExtensionContainer</string></field>
                <field name="rank"><int>1000</int></field>
                <field name="name"><string>ViewSource</string></field>
                <field name="component"><string>com.acme.ViewSourceActionComponent</string></field>
              </object>
            </object-param>
          </init-params>
        </component-plugin>
      </external-component-plugins>

    </configuration>

Besides the MoreExtensionContainer type, action extension may be one of these following types:

Type | Description
--- | ---
`org.exoplatform.wiki.webui.control.UIPageToolBar` | Actions will be placed on the Wiki page toolbar at the view mode.
`org.exoplatform.wiki.webui.control.AddExtensionContainer` | Actions will be placed on the Add Page drop-down list on the Wiki page toolbar at the view mode.
`org.exoplatform.wiki.webui.control.MoreExtensionContainer` | Actions will be placed on the More drop-down list on the Wiki page toolbar at the view mode.
`org.exoplatform.wiki.webui.control.UISubmitToolBar` | Actions will be placed on the Wiki page toolbar at the edit mode.
`org.exoplatform.wiki.webui.control.UIEditorTabs` | Actions will be placed the on Wiki editor tabs.
`org.exoplatform.wiki.webui.control.BrowseExtensionContainer` | Actions will be placed on the Browse drop-down list at the view mode.
`org.exoplatform.wiki.webui.popup.UIWikiSettingContainer` | Actions will be placed on the Wiki setting tabs.

##### Deploy new action extension

Follow these steps to deploy your new action extension:

1. Build the project by using the mvn clean install command.

2. Copy the target/example-1.0-SNAPSHOT.jar file into the TOMCATHOME/webapps/wiki.war/WEB-INF/lib/ directory.

3. Run the tomcat and go to Wiki application, you should see the result as below: