Discussions Application API

Last modified by Admin on 2026/06/29 00:02

cogAPI of the discussions application.
TypeJAR
CategoryAPI
Developed by

Simon Urli, Manuel Leduc

Active Installs0
Rating
0 Votes
LicenseGNU Lesser General Public License 2.1
Compatibility

14.10+

Success

Installable with the Extension Manager

Description

The Discussions API allows to interact with the Discussions Application data model.
It can be used to query existing discussion, but also to integrates new kinds of discussions and new kinds of discussions into XWiki.

Discussion Context Service

The discussions context service provides the operations to get and create discussions contexts as well as link discussions and discussion contexts together.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions;

import java.util.List;
import java.util.Optional;
import java.util.Map;

import org.xwiki.component.annotation.Role;
import org.xwiki.contrib.discussions.domain.Discussion;
import org.xwiki.contrib.discussions.domain.DiscussionContext;
import org.xwiki.contrib.discussions.domain.references.DiscussionContextEntityReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionContextReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionReference;

/**
 * This service provides the operation to manipulate discussion context objects.
 *
 * @version $Id$
 * @since 1.0
 */
@Role
public interface DiscussionContextService
{
    /**
     * Creates a discussion context.
     *
     * @param applicationHint the hint of the application used to create the context
     * @param name the discussion context name
     * @param description the discussion context description
     * @param entityReference the reference of the entity referenced by the discussion context
     * @param configurationParameters parameters used for data storage configuration
     * @return the initialized discussion context
     */
    DiscussionContext create(String applicationHint, String name, String description,
        DiscussionContextEntityReference entityReference,
        DiscussionStoreConfigurationParameters configurationParameters) throws DiscussionException;

    /**
     * If a discussion context already exist with the given reference type and entity reference, it is returned. If it
     * does not the discussion context is created with the name and description passed in parameter, and it then
     * returned.
     *
     * @param applicationHint the hint of the application used to create the context
     * @param name the name
     * @param description the description
     * @param entityReference the entity reference
     * @param configurationParameters parameters used for data storage configuration
     * @return the found or created discussion context
     */
    DiscussionContext getOrCreate(String applicationHint, String name, String description,
        DiscussionContextEntityReference entityReference,
        DiscussionStoreConfigurationParameters configurationParameters) throws DiscussionException;

    /**
     * Check if a {@link DiscussionContext} already exists for the given entity reference.
     * @param entityReference the reference for which to check if a context already exists.
     * @return {@code true} only if the context already exists
     * @since 3.1.0
     */
    default boolean existsFor(DiscussionContextEntityReference entityReference)
    {
        return false;
    }

    /**
     * Link a discussion context and a discussion.
     *
     * @param discussionContext the discussion context
     * @param discussion the discussion
     */
    void link(DiscussionContext discussionContext, Discussion discussion);

    /**
     * Unlink a discussion context and a discussion.
     *
     * @param discussionContext the discussion context
     * @param discussion the discussion
     */
    void unlink(DiscussionContext discussionContext, Discussion discussion);

    /**
     * Search and retrieve a discussion context by its reference.
     *
     * @param reference the discussion context reference
     * @return the discussion context
     */
    Optional<DiscussionContext> get(DiscussionContextReference reference);

    /**
     * Returns the list of discussion contexts linked to the discussion reference.
     *
     * @param reference the discussion reference
     * @return the list of discussion contexts
     */
    List<DiscussionContext> findByDiscussionReference(DiscussionReference reference);

    /**
     * @return {@code true} if the current actor can create a discussion context, {@code false} otherwise
     */
    boolean canCreateDiscussionContext();

    /**
     * @param reference the reference of the discussion context
     * @return {@code true} if the current user can view the discussion context, {@code false} otherwise
     */
    boolean canViewDiscussionContext(DiscussionContextReference reference);

    /**
     * Save a new metadata to be added in the discussion context.
     * This method both puts the metadata in {@link DiscussionContext#getMetadata()} map and also persists it
     * permanently.
     *
     * @param context the context for which to add the new metadata.
     * @param values the map of values to save
     * @return {@code true} if the metadata was properly saved.
     */
    boolean saveMetadata(DiscussionContext context, Map<String, String> values);
}

Discussion Service

The discussions service provides the operations to get and create discussions and as well a select discussions according to the discussions contexts to which they are linked.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions;

import java.util.List;
import java.util.Optional;

import org.xwiki.component.annotation.Role;
import org.xwiki.contrib.discussions.domain.Discussion;
import org.xwiki.contrib.discussions.domain.references.DiscussionContextEntityReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionContextReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionReference;

/**
 * This service provides the operation to manipulate discussion objects.
 *
 * @version $Id$
 * @since 1.0
 */
@Role
public interface DiscussionService
{
    /**
     * Creates a discussion with a main document.
     *
     * @param applicationHint the hint of the application used to create the discussion
     * @param title the discussion title
     * @param description the discussion description
     * @param mainDocument the main document to view the discussion
     * @param configurationParameters parameters used for data storage configuration
     * @return the created discussion
     */
    Discussion create(String applicationHint, String title, String description, String mainDocument,
        DiscussionStoreConfigurationParameters configurationParameters) throws DiscussionException;

    /**
     * Search for a discussion linked to the provided list of discussion contexts. If it exists, it is directly
     * returned. If it does not, it is initialized with the provided title and description, and linked to the list of
     * discussion contexts.
     *
     * @param applicationHint the hint of the application used to create the discussion
     * @param title the title
     * @param description the description
     * @param discussionContexts the list of discussion contexts
     * @param configurationParameters parameters used for data storage configuration
     * @return the created or found discussion
     */
    Discussion getOrCreate(String applicationHint, String title, String description,
        List<DiscussionContextReference> discussionContexts,
        DiscussionStoreConfigurationParameters configurationParameters) throws DiscussionException;

    /**
     * Search and retrieve a discussion by its reference.
     *
     * @param reference the discussion reference
     * @return the discussion
     */
    Optional<Discussion> get(DiscussionReference reference);

    /**
     * @param reference a discussion reference
     * @return {@code true} if the current user has the right to view the discussion, {@code false} otherwise
     */
    boolean canRead(DiscussionReference reference);

    /**
     * @param reference a discussion reference
     * @return {@code true} if the current user has the rights to write in the discussion, {@code false} otherwise
     */
    boolean canWrite(DiscussionReference reference);

    /**
     * Find a list of discussions that are linked at least to the list of discussion context passed in parameter.
     *
     * @param discussionContextReferences a list of discussion context references
     * @return the list discussions attached to the list of discussion contexts
     */
    List<Discussion> findByDiscussionContexts(List<DiscussionContextReference> discussionContextReferences);

    /**
     * Count the number of discussions linked to a context with the given entity references values.
     *
     * @param type the type of the entity reference
     * @param references the reference values of the entity references
     * @return the count result
     */
    long countByEntityReferences(String type, List<String> references);

    /**
     * Find the list of discussions linked to discussion contexts with the given entity references.
     *
     * @param type the entity reference type
     * @param references the entity reference values
     * @param offset the offset
     * @param limit the limit
     * @return tge paginated list of discussions
     */
    List<Discussion> findByEntityReferences(String type, List<String> references, Integer offset, Integer limit);

    /**
     * Set the update date of the discussion to now.
     *
     * @param discussionReference the reference of the discussion to update
     */
    void touch(DiscussionReference discussionReference);

    /**
     * Return true if a discussion exists with the request discussion context entity.
     *
     * @param reference the reference of the discussion context entity
     * @return {@code true} if a discussion context is found, {@code false} otherwise
     */
    boolean findByDiscussionContext(DiscussionContextEntityReference reference);

    /**
     * @return {@code true} if the current user can create a discussion, {@code false} otherwise
     */
    boolean canCreateDiscussion();

    /**
     * @param reference the reference of the discussion
     * @return {@code true} if the current user is allowed to view the discussion, {@code false} otherwise
     */
    boolean canViewDiscussion(DiscussionReference reference);
}

Message Service

The message services provides the operations to get, create and count messages.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions;

import java.util.List;
import java.util.Optional;

import org.xwiki.component.annotation.Role;
import org.xwiki.contrib.discussions.domain.Discussion;
import org.xwiki.contrib.discussions.domain.Message;
import org.xwiki.contrib.discussions.domain.references.ActorReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionReference;
import org.xwiki.contrib.discussions.domain.references.MessageReference;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.rendering.syntax.Syntax;

/**
 * This service provides the operation to manipulate message objects.
 *
 * @version $Id$
 * @since 1.0
 */
@Role
public interface MessageService
{
    /**
     * Creates a message for the current user.
     *
     * @param content the message content
     * @param syntax the syntax of the content of the message
     * @param discussionReference the discussion reference
     * @param configurationParameters parameters used for data storage configuration
     * @return the created message
     */
    Message create(String content, Syntax syntax, DiscussionReference discussionReference,
        DiscussionStoreConfigurationParameters configurationParameters) throws DiscussionException;

    /**
     * Creates a message for a specific user.
     *
     * @param content the message content
     * @param syntax the syntax of the content of the message
     * @param discussionReference the discussion reference
     * @param authorReference the author reference
     * @param configurationParameters parameters used for data storage configuration
     * @return the create message
     */
    Message create(String content, Syntax syntax, DiscussionReference discussionReference,
        ActorReference authorReference, DiscussionStoreConfigurationParameters configurationParameters)
        throws DiscussionException;

    /**
     * Creates a message for a specific user.
     *
     * @param content the message content
     * @param syntax the syntax of the content of the message
     * @param discussionReference the discussion reference
     * @param authorReference the author reference
     * @param notify {@code true} if the notifications for the message creation can be sent, {@code false}
     *     otherwise
     * @param configurationParameters parameters used for data storage configuration
     * @return the create message
     */
    Message create(String content, Syntax syntax, DiscussionReference discussionReference,
        ActorReference authorReference, boolean notify, DiscussionStoreConfigurationParameters configurationParameters)
        throws DiscussionException;

    /**
     * Creates a message in reply to an existing message.
     *
     * @param content the message content
     * @param syntax the syntax of the content of the message
     * @param originalMessage the existing message this message replies to
     * @param authorReference the author reference
     * @param notify {@code true} if the notifications for the message creation can be sent, {@code false}
     *     otherwise
     * @param configurationParameters parameters used for data storage configuration
     * @return the create message
     */
    default Message createReplyTo(String content, Syntax syntax, Message originalMessage,
        ActorReference authorReference, boolean notify, DiscussionStoreConfigurationParameters configurationParameters)
        throws DiscussionException
    {
        return null;
    }

    /**
     * Get a message by its unique reference.
     *
     * @param reference the reference
     * @return the message
     */
    Optional<Message> getByReference(MessageReference reference);

    /**
     * Returns the paginated list of messages of the discussion.
     *
     * @param discussionReference the discussion reference
     * @param offset the offset
     * @param limit the limit
     * @return the list of messages
     */
    List<Message> getByDiscussion(DiscussionReference discussionReference, int offset, int limit);

    /**
     * Returns the count of messages of a discussion.
     *
     * @param discussion the discussion
     * @return the count of messages
     */
    long countByDiscussion(Discussion discussion);

    /**
     * Checks if the message can be deleted by the current user.
     *
     * @param message the message
     * @return {@code true} of the current user can delete the message. {@code false} otherwise
     */
    boolean canDelete(Message message);

    /**
     * Delete a message.
     *
     * @param reference the message reference
     */
    void delete(MessageReference reference);

    /**
     * Safely renders the content of a message.
     *
     * @param messageReference the reference of the message to render
     * @return the result of the rendering in html
     */
    String renderContent(MessageReference messageReference);

    /**
     * Load a message object by its entity reference.
     *
     * @param entityReference the entity reference of the message object
     * @return the message object
     */
    Optional<Message> getByEntity(EntityReference entityReference);
}

Discussions Rights Service

The discussions rights services provides the right operations for the discussions entities.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions;

import org.xwiki.component.annotation.Role;
import org.xwiki.contrib.discussions.domain.Discussion;
import org.xwiki.contrib.discussions.domain.Message;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.security.authorization.RuleState;

/**
 * Service to manage the rights of the discussions entities.
 *
 * @version $Id$
 * @since 1.0
 */
@Role
public interface DiscussionsRightService
{
    /**
     * @return {@code true} if the current user can create a discussion, {@code false} otherwise
     */
    boolean canCreateDiscussion();

    /**
     * @return {@code true} if the current user can create a discussion context, {@code false} otherwise
     */
    boolean canCreateDiscussionContext();

    /**
     * @param discussion the discussion page
     * @return {@code true} of the current user can read the discussion, {@code false otherwise}
     */
    boolean canReadDiscussion(EntityReference discussion);

    /**
     * @param discussion the discussion page
     * @return {@code true} if the current user can write in the discussion, {@code false} otherwise.
     */
    boolean canWriteDiscussion(DocumentReference discussion);

    /**
     * @param discussionContext the discussion context page
     * @return {@code true} if the current user can write in the discussion context, {@code false} otherwise.
     */
    boolean canWriteDiscussionContext(DocumentReference discussionContext);

    /**
     * @param message the message
     * @param discussion the discussion page
     * @return {@code true} if the current user can delete the message, {@code false} otherwise
     */
    boolean canDeleteMessage(Message message, DocumentReference discussion);

    /**
     * @param discussion the discussion
     * @return {@code} true if the current user is an administrator of the discussion, {@code} false otherwise
     */
    boolean isAdminDiscussion(DocumentReference discussion);

    /**
     * Define the user right to read the discussion.
     *
     * @param discussion the discussion
     * @param user the user
     * @param state define if the right should be allowed or denied
     */
    void setRead(Discussion discussion, DocumentReference user, RuleState state);

    /**
     * Allow the user to write the discussion.
     *
     * @param discussion the discussion
     * @param user the user
     * @param state define if the right should be allowed or denied
     */
    void setWrite(Discussion discussion, DocumentReference user, RuleState state);
}

Discussion Actor Service

The discussions actor service provides the resolve operation to resolve a discussion actor according to its reference.
The type of the actor is used as an hint to find the discussion actor component to use to resolve an actor reference.

For instance, for an actor of type user and identifier xwiki:XWiki.U1, the UserDiscussionsActorsService will be used.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions;

import java.util.Optional;
import java.util.stream.Stream;

import org.xwiki.component.annotation.Role;
import org.xwiki.contrib.discussions.domain.ActorDescriptor;
import org.xwiki.contrib.discussions.domain.references.DiscussionReference;

/**
 * Returns an actor descriptor from an actor reference.
 *
 * @version $Id$
 * @since 1.0
 */
@Role
public interface DiscussionsActorService
{
    /**
     * Returns an actor descriptor from an actor reference.
     *
     * @param reference the actor reference
     * @return the actor description, or {@link Optional#empty()} in case of error during the resolution
     */
    Optional<ActorDescriptor> resolve(String reference);

    /**
     * Returns the list of actors involved in a discussion. The definition of this involvement is up to interpretation
     * and can be implemented freely by services that implement this role.
     *
     * @param discussionReference the discussion reference
     * @return the list of actors involved in a discuission
     */
    Stream<ActorDescriptor> listUsers(DiscussionReference discussionReference);

    /**
     * Returns the number of users involved in a discussion.
     *
     * @param discussionReference the discussion reference
     * @return the number of users involved in the request discussion
     */
    long countUsers(DiscussionReference discussionReference);
}

Discussions Actor Service Resolver

The discussion actor service resolve provides the operations to get the discussion actor service for the requested type.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions;

import org.xwiki.component.annotation.Role;

/**
 * Resolve a {@link DiscussionsActorService} according to the requested actor type.
 *
 * @version $Id$
 * @since 1.0
 */
@Role
public interface DiscussionsActorServiceResolver
{
    /**
     * Returns an actor service for the requested type.
     * In case the actor service cannot be found  for the given type, fallback on a default service.
     *
     * @param type the type
     * @return the actor service corresponding to the type, or default service
     */
    DiscussionsActorService get(String type);
}

Discussions Script Service

The discussions script service provides the operations to interact with the discussions data model from XWiki Scripting.

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions.script;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.contrib.discussions.DiscussionContextService;
import org.xwiki.contrib.discussions.DiscussionException;
import org.xwiki.contrib.discussions.DiscussionReferencesResolver;
import org.xwiki.contrib.discussions.DiscussionReferencesSerializer;
import org.xwiki.contrib.discussions.DiscussionService;
import org.xwiki.contrib.discussions.DiscussionStoreConfigurationParameters;
import org.xwiki.contrib.discussions.DiscussionsActorServiceResolver;
import org.xwiki.contrib.discussions.MessageService;
import org.xwiki.contrib.discussions.domain.ActorDescriptor;
import org.xwiki.contrib.discussions.domain.Discussion;
import org.xwiki.contrib.discussions.domain.DiscussionContext;
import org.xwiki.contrib.discussions.domain.references.ActorReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionContextEntityReference;
import org.xwiki.contrib.discussions.domain.Message;
import org.xwiki.contrib.discussions.domain.references.AbstractDiscussionReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionContextReference;
import org.xwiki.contrib.discussions.domain.references.DiscussionReference;
import org.xwiki.contrib.discussions.domain.references.MessageReference;
import org.xwiki.contrib.discussions.internal.QueryStringService;
import org.xwiki.contrib.discussions.store.MessageHolderReferenceService;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.parser.ParseException;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.script.service.ScriptService;
import org.xwiki.script.service.ScriptServiceManager;
import org.xwiki.stability.Unstable;

import static java.util.Collections.singletonList;
import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage;

/**
 * Discussions script service.
 *
 * @version $Id$
 * @since 1.0
 */
@Named(DiscussionsScriptService.ROLEHINT)
@Component
@Singleton
public class DiscussionsScriptService implements ScriptService
{
    /**
     * The role hint of this component.
     */
    public static final String ROLEHINT = "discussions";

    @Inject
    private DiscussionContextService discussionContextService;

    @Inject
    private DiscussionService discussionService;

    @Inject
    private MessageService messageService;

    @Inject
    private QueryStringService queryStringService;

    @Inject
    private DiscussionsActorServiceResolver actorsServiceResolver;

    @Inject
    private ScriptServiceManager scriptServiceManager;

    @Inject
    private Logger logger;

    @Inject
    private DiscussionReferencesResolver discussionReferencesResolver;

    @Inject
    private DiscussionReferencesSerializer discussionReferencesSerializer;

    @Inject
    private MessageHolderReferenceService messageHolderReferenceService;

    /**
     * Creates a discussion context.
     *
     * @param applicationHint the application in which the discussion has been created.
     * @param name the name
     * @param description the description
     * @param referenceType the entity reference type
     * @param entityReference the entity reference
     * @param storeConfigurationParameters parameters used for configuration storage
     * @return the created discussion context
     */
    public DiscussionContext createDiscussionContext(String applicationHint, String name, String description,
        String referenceType, String entityReference, Map<String, Object> storeConfigurationParameters)
        throws DiscussionException
    {
        if (this.discussionContextService.canCreateDiscussionContext()) {
            return this.discussionContextService.create(applicationHint, name, description,
                new DiscussionContextEntityReference(referenceType, entityReference),
                new DiscussionStoreConfigurationParameters(storeConfigurationParameters));
        } else {
            return null;
        }
    }

    /**
     * Get a discussion context. Creates it if it does not already exist.
     *
     * @param applicationHint the application in which the discussion context has been created.
     * @param name the discussion context name
     * @param description the discussion context description
     * @param referenceType the entity reference type
     * @param entityReference the entity reference
     * @param storeConfigurationParameters parameters used for configuration storage
     * @return the request discussion context
     */
    public DiscussionContext getOrCreateDiscussionContext(String applicationHint, String name, String description,
        String referenceType, String entityReference, Map<String, Object> storeConfigurationParameters)
        throws DiscussionException
    {
        if (this.discussionContextService.canCreateDiscussionContext()) {
            return this.discussionContextService.getOrCreate(applicationHint, name, description,
                    new DiscussionContextEntityReference(referenceType, entityReference),
                    new DiscussionStoreConfigurationParameters(storeConfigurationParameters));
        } else {
            return null;
        }
    }

    /**
     * Creates a discussion with an URL to the main discussion view page.
     *
     * @param applicationHint the application in which the discussion has been created.
     * @param title the discussion title
     * @param description the discussion description
     * @param mainDocument the main document to view the discussion
     * @param storeConfigurationParameters parameters used for configuration storage
     * @return the created discussion
     */
    public Discussion createDiscussion(String applicationHint, String title, String description, String mainDocument,
        Map<String, Object> storeConfigurationParameters) throws DiscussionException
    {
        return this.discussionService.create(applicationHint, title, description, mainDocument,
            new DiscussionStoreConfigurationParameters(storeConfigurationParameters));
    }

    /**
     * Retrieve a discussion by its reference.
     *
     * @param reference the discussion reference
     * @return the discussion, {@code null} if not found
     */
    public Discussion getDiscussion(String reference)
    {
        DiscussionReference discussionReference =
            this.discussionReferencesResolver.resolve(reference, DiscussionReference.class);
        if (this.discussionService.canViewDiscussion(discussionReference)) {
            return this.discussionService.get(discussionReference).orElse(null);
        } else {
            return null;
        }
    }

    /**
     * Retrieve a discussion context by its reference.
     *
     * @param reference the discussion context reference
     * @return the discussion context, {@code null} if not found
     */
    public DiscussionContext getDiscussionContext(String reference)
    {
        DiscussionContextReference discussionContextReference =
            this.discussionReferencesResolver.resolve(reference, DiscussionContextReference.class);
        if (this.discussionContextService.canViewDiscussionContext(discussionContextReference)) {
            return this.discussionContextService.get(discussionContextReference).orElse(null);
        } else {
            return null;
        }
    }

    /**
     * Create a message in a discussion for the current user.
     *
     * @param content the content
     * @param syntax the syntax of the content of the message
     * @param reference the discussion reference
     * @param storeConfigurationParameters parameters used for configuration storage
     * @return the created message
     */
    public Message createMessage(String content, String syntax, String reference,
        Map<String, Object> storeConfigurationParameters) throws DiscussionException
    {
        DiscussionReference discussionReference =
            this.discussionReferencesResolver.resolve(reference, DiscussionReference.class);
        if (this.discussionService.canWrite(discussionReference)) {
            try {
                return this.messageService.create(content, Syntax.valueOf(syntax), discussionReference,
                        new DiscussionStoreConfigurationParameters(storeConfigurationParameters));
            } catch (ParseException e) {
                this.logger.warn("Malformed syntax [{}]. Cause: [{}].", syntax, getRootCauseMessage(e));
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Retrieve the reference of the next message to be created: this reference should be used for temporary
     * attachments uploads.
     *
     * @param serializedDiscussionReference the discussion for which to obtain a new message holder reference
     * @param storeConfigurationParameters the related configuration parameters
     * @return a reference to be used for displaying the message editor
     * @since 2.1
     * @see MessageHolderReferenceService
     */
    @Unstable
    public DocumentReference getNextMessageHolderReference(String serializedDiscussionReference,
        Map<String, Object> storeConfigurationParameters)
    {
        DiscussionReference discussionReference =
            this.discussionReferencesResolver.resolve(serializedDiscussionReference, DiscussionReference.class);
        return this.messageHolderReferenceService.getNextMessageHolderReference(discussionReference,
            new DiscussionStoreConfigurationParameters(storeConfigurationParameters));
    }

    /**
     * Return a paginated list of messages of a discussion.
     *
     * @param discussion the discussion
     * @param offset the offset
     * @param limit the limit
     * @return the messages of the discussion
     */
    public List<Message> getMessagesByDiscussion(Discussion discussion, int offset, int limit)
    {
        if (this.discussionService.canViewDiscussion(discussion.getReference())) {
            return this.messageService.getByDiscussion(discussion.getReference(), offset * limit, limit);
        } else {
            return null;
        }
    }

    /**
     * Return the number of messages in a discussion.
     *
     * @param discussion the discussion
     * @return the messages count of the discussion
     */
    public long countMessagesByDiscussion(Discussion discussion)
    {
        if (this.discussionService.canViewDiscussion(discussion.getReference())) {
            return this.messageService.countByDiscussion(discussion);
        } else {
            return 0;
        }
    }

    /**
     * Update a param with newParameterMap values and returns a string representation.
     *
     * @param parameterMap the query string
     * @param newParameterMap the new parameters to overload or add
     * @return the string representation
     */
    public String updateQueryString(Map<String, Object> parameterMap, Map<String, Object> newParameterMap)
    {
        return this.queryStringService.getString(parameterMap, newParameterMap);
    }

    /**
     * Find the discussions linked to exactly the provided list of discussion context reference.
     *
     * @param discussionContextReferences the list of discussion context reference
     * @return the list of discussions
     */
    public List<Discussion> findByDiscussionContexts(List<String> discussionContextReferences)
    {
        return this.discussionService.findByDiscussionContexts(
            discussionContextReferences
                .stream()
                .map(ref -> this.discussionReferencesResolver.resolve(ref, DiscussionContextReference.class))
                .collect(Collectors.toList()));
    }

    /**
     * Links a discussion and a discussion context.
     *
     * @param discussion the discussion
     * @param discussionContext the discussion context
     */
    public void linkDiscussionToDiscussionContext(Discussion discussion, DiscussionContext discussionContext)
    {
        this.discussionContextService.link(discussionContext, discussion);
    }

    /**
     * Unlinks a discussion and a discussion context.
     *
     * @param discussion the discussion
     * @param discussionContext the discussion context
     */
    public void unlinkDiscussionToDiscussionContext(Discussion discussion, DiscussionContext discussionContext)
    {
        this.discussionContextService.unlink(discussionContext, discussion);
    }

    /**
     * Returns an actor descriptor for the provided reference according to its type.
     *
     * @param type the type of the actor
     * @param reference the reference of the actor
     * @return the {@link ActorDescriptor}, or {@code null} in case of error during the resolution
     */
    public ActorDescriptor getActorDescriptor(String type, String reference)
    {
        return this.actorsServiceResolver.get(type).resolve(reference).orElse(null);
    }

    /**
     * Returns an actor descriptor for the provided actor reference.
     *
     * @param actorReference the reference of the actor
     * @return the {@link ActorDescriptor}, or {@code null} in case of error during the resolution
     */
    public ActorDescriptor getActorDescriptor(ActorReference actorReference)
    {
        return this.getActorDescriptor(actorReference.getType(), actorReference.getReference());
    }

    /**
     * @param <S> the type of the {@link ScriptService}
     * @param serviceName the name of the sub {@link ScriptService}
     * @return the {@link ScriptService} or null of none could be found
     */
    @SuppressWarnings("unchecked")
    public <S extends ScriptService> S get(String serviceName)
    {
        return (S) this.scriptServiceManager.get(ROLEHINT + '.' + serviceName);
    }

    /**
     * Safely renders the content of the message.
     *
     * @param messageReference the message reference
     * @return the content of the message rendered in html
     */
    public String renderMessageContent(String messageReference)
    {
        MessageReference reference =
            this.discussionReferencesResolver.resolve(messageReference, MessageReference.class);
        return this.messageService.renderContent(reference);
    }

    /**
     * Checks if the provided discussion reference is linked to the request entity type and entity reference.
     *
     * @param discussionReference a discussion reference
     * @param entityType an entity type
     * @param entityReference an entity reference
     * @return {@code true} if the discussion is linked to an discussion context with the required entity type and
     *     entity reference
     */
    public boolean hasDiscussionContext(String discussionReference, String entityType, String entityReference)
    {
        DiscussionReference reference =
            this.discussionReferencesResolver.resolve(discussionReference, DiscussionReference.class);
        return this.discussionService.findByEntityReferences(entityType, singletonList(entityReference), null, null)
            .stream()
            .anyMatch(it -> it.getReference().equals(reference));
    }

    /**
     * Returns the first discussion found which is link to a discussion context with the provided entity type and entity
     * reference.
     *
     * @param entityType the discussion context entity type
     * @param entityReference the discussion context entity reference
     * @return a discussion linked to the request discussion context
     * @since 1.1
     */
    public Discussion getDiscussionByDiscussionContext(String entityType, String entityReference)
    {
        List<Discussion> discussions =
            this.discussionService.findByEntityReferences(entityType, singletonList(entityReference), 0, 1);
        Discussion discussion;
        if (discussions.isEmpty()) {
            discussion = null;
        } else {
            discussion = discussions.get(0);
        }

        return discussion;
    }

    /**
     * Allow to serialize the given reference.
     *
     * @param reference the reference to be serialized.
     * @param <T> the actual concrete type of the reference.
     * @return a serialization of the reference.
     * @since 2.0
     */
    @Unstable
    public <T extends AbstractDiscussionReference> String serialize(T reference)
    {
        return this.discussionReferencesSerializer.serialize(reference);
    }

    /**
     * Retrieve a message by reference.
     * @param reference the reference of the message.
     * @return the retrieved message or null.
     * @since 2.0
     */
    @Unstable
    public Message getMessage(MessageReference reference)
    {
        return this.messageService.getByReference(reference).orElse(null);
    }
}

Discussions Rights Script Service

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.contrib.discussions.script;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.contrib.discussions.DiscussionReferencesResolver;
import org.xwiki.contrib.discussions.DiscussionService;
import org.xwiki.contrib.discussions.DiscussionsRightService;
import org.xwiki.contrib.discussions.MessageService;
import org.xwiki.contrib.discussions.domain.Discussion;
import org.xwiki.contrib.discussions.domain.Message;
import org.xwiki.contrib.discussions.domain.references.DiscussionReference;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.script.service.ScriptService;
import org.xwiki.security.authorization.ContextualAuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.security.authorization.RuleState;
import org.xwiki.stability.Unstable;

/**
 * Script service dedicated to the discussions rights.
 *
 * @version $Id$
 * @since 1.0
 */
@Unstable
@Component
@Named("discussions.rights")
@Singleton
public class DiscussionRightsScriptService implements ScriptService
{
    @Inject
    private DiscussionService discussionService;

    @Inject
    private MessageService messageService;

    @Inject
    private DiscussionsRightService discussionsRightService;

    @Inject
    private ContextualAuthorizationManager authorizationManager;

    @Inject
    private DiscussionReferencesResolver discussionReferencesResolver;

    /**
     * @param discussionReference the discussion reference
     * @return {@code true} if the current user can read the discussion, {@code false} otherwise
     */
    public boolean canReadDiscussion(String discussionReference)
    {
        DiscussionReference reference =
            this.discussionReferencesResolver.resolve(discussionReference, DiscussionReference.class);
        return this.discussionService.canRead(reference);
    }

    /**
     * @param discussionReference the discussion reference
     * @return {@code true} if the current user can write in the discussion, {@code false} otherwise
     */
    public boolean canWriteDiscussion(String discussionReference)
    {
        DiscussionReference reference =
            this.discussionReferencesResolver.resolve(discussionReference, DiscussionReference.class);
        return this.discussionService.canWrite(reference);
    }

    /**
     * @param message the message
     * @return {@code true} if the current user can remove the message, {@code false} otherwise
     */
    public boolean canDeleteMessage(Message message)
    {
        return this.messageService.canDelete(message);
    }

    /**
     * Allows a user to read a discussion. This operation requires the programming right.
     *
     * @param discussion a discussion
     * @param user a user
     * @param allow use {@code true} to allow the right and {@code false} to deny it.
     */
    public void setRead(Discussion discussion, DocumentReference user, boolean allow)
    {
        if (this.authorizationManager.hasAccess(Right.PROGRAM)) {
            RuleState state = (allow) ? RuleState.ALLOW : RuleState.DENY;
            this.discussionsRightService.setRead(discussion, user, state);
        }
    }

    /**
     * Allows a user to write on a discussion. This operation requires the programming right.
     *
     * @param discussion a discussion
     * @param user a user
     * @param allow use {@code true} to allow the right and {@code false} to deny it.
     */
    public void setWrite(Discussion discussion, DocumentReference user, boolean allow)
    {
        if (this.authorizationManager.hasAccess(Right.PROGRAM)) {
            RuleState state = (allow) ? RuleState.ALLOW : RuleState.DENY;
            this.discussionsRightService.setWrite(discussion, user, state);
        }
    }
}

Scripting Examples

{{velocity}}
## Create a discussion context and a discussion and links the together.
#set ($discussionContext = $services.discussions.createDiscussionContext('', '', 'example', 'contextA'))
#set ($discussion = $services.discussions.createDiscussion('New discusssion', 'This is a new example discussion.'))
#set ($discard = $services.discussions.linkDiscussionToDiscussionContext($discussion, $discussionContext))
## Add a message to the discussion.
#set ($message = $services.discussions.createMessage('**Message** content', 'xwiki/2.1', $discussion)
## Get the discussions linked to some discussion contexts
$services.discussions.findByDiscussionContexts([$discussionContext.reference, 'reference2'])
## Checks if the current user can view the discussion
$services.discussions.rights.canReadDiscussion($discussion.reference)
{{/velocity}}

Prerequisites & Installation Instructions

We recommend using the Extension Manager to install this extension (Make sure that the text "Installable with the Extension Manager" is displayed at the top right location on this page to know if this extension can be installed with the Extension Manager).

You can also use the manual method which involves dropping the JAR file and all its dependencies into the WEB-INF/lib folder and restarting XWiki.

Versions

Dependencies

Dependencies for this extension (org.xwiki.contrib:discussions-api 3.1.0):

Get Connected