/*
* 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 com.xpn.xwiki.plugin.activitystream.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.hibernate.Session;
import org.xwiki.component.annotation.Component;
import org.xwiki.eventstream.Event;
import org.xwiki.eventstream.EventStatus;
import org.xwiki.eventstream.EventStatusManager;
import org.xwiki.eventstream.internal.DefaultEventStatus;
import org.xwiki.query.Query;
import org.xwiki.query.QueryManager;
import org.xwiki.text.StringUtils;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.plugin.activitystream.api.ActivityEventStatus;
import com.xpn.xwiki.plugin.activitystream.api.ActivityStreamException;
import com.xpn.xwiki.plugin.activitystream.eventstreambridge.EventConverter;
import com.xpn.xwiki.plugin.activitystream.impl.ActivityStreamConfiguration;
import com.xpn.xwiki.store.XWikiHibernateStore;
/**
* Default implementation of {@link EventStatusManager} which use the Activity Stream storage.
*
* @version $Id: 4225358aac367229396684bd42f3e8d6e6ba9704 $
* @since 9.2RC1
*/
@Component
@Singleton
public class DefaultEventStatusManager implements EventStatusManager
{
@Inject
private QueryManager queryManager;
@Inject
private EventConverter eventConverter;
@Inject
private ActivityStreamConfiguration configuration;
@Inject
private Provider<XWikiContext> contextProvider;
@Override
public List<EventStatus> getEventStatus(List<Event> events, List<String> entityIds) throws Exception
{
List<EventStatus> results = new ArrayList<>();
// Don't perform any query if the list of events or entities is actually empty
if (events.isEmpty() || entityIds.isEmpty()) {
return results;
}
// Get the ActivityEventStatus from the database and convert them
Query query = queryManager.createQuery("select eventStatus from ActivityEventStatusImpl eventStatus "
+ "where eventStatus.activityEvent.id in :eventIds and eventStatus.entityId in :entityIds", Query.HQL);
query.bindValue("eventIds", getEventIds(events));
query.bindValue("entityIds", entityIds);
for (ActivityEventStatus activityEventStatus : query.<ActivityEventStatus>execute()) {
results.add(new DefaultEventStatus(
eventConverter.convertActivityToEvent(activityEventStatus.getActivityEvent()),
activityEventStatus.getEntityId(),
activityEventStatus.isRead())
);
}
// For status that are not present in the database, we create objects with read = false
for (Event event : events) {
for (String entityId : entityIds) {
if (!isPresent(event, entityId, results)) {
results.add(new DefaultEventStatus(event, entityId, false));
}
}
}
// Sort statuses by date, in the descending order, like notifications, otherwise the order is lost
Collections.sort(results,
(status1, status2) -> status2.getEvent().getDate().compareTo(status1.getEvent().getDate()));
return results;
}
/**
* @param events a list of events
* @return the list of the events' id
*/
private List<String> getEventIds(List<Event> events)
{
List<String> eventIds = new ArrayList<>();
for (Event event : events) {
eventIds.add(event.getId());
}
return eventIds;
}
/**
* @return if there is a status concerning the given event and the given entity in a list of statuses
*/
private boolean isPresent(Event event, String entityId, List<EventStatus> list)
{
for (EventStatus status : list) {
if (StringUtils.equals(status.getEvent().getId(), event.getId())
&& StringUtils.equals(status.getEntityId(), entityId)) {
return true;
}
}
return false;
}
@Override
public void saveEventStatus(EventStatus eventStatus) throws Exception
{
ActivityEventStatus status = eventConverter.convertEventStatusToActivityStatus(eventStatus);
if (configuration.useLocalStore()) {
saveEventStatusInStore(status);
}
if (configuration.useMainStore()) {
XWikiContext context = contextProvider.get();
// store event in the main database
String oriDatabase = context.getWikiId();
context.setWikiId(context.getMainXWiki());
try {
saveEventStatusInStore(status);
} finally {
context.setWikiId(oriDatabase);
}
}
}
private void saveEventStatusInStore(ActivityEventStatus eventStatus) throws ActivityStreamException
{
XWikiContext context = contextProvider.get();
XWikiHibernateStore hibernateStore = context.getWiki().getHibernateStore();
try {
hibernateStore.beginTransaction(context);
Session session = hibernateStore.getSession(context);
session.save(eventStatus);
hibernateStore.endTransaction(context, true);
} catch (XWikiException e) {
hibernateStore.endTransaction(context, false);
throw new ActivityStreamException(e);
}
}
}