001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.repository.activities; 017 018import java.time.ZonedDateTime; 019import java.time.temporal.ChronoUnit; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.configuration.Configurable; 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030 031import org.ametys.core.user.CurrentUserProvider; 032import org.ametys.core.user.UserIdentity; 033import org.ametys.plugins.core.user.UserHelper; 034import org.ametys.runtime.i18n.I18nizableText; 035import org.ametys.runtime.model.type.DataContext; 036import org.ametys.runtime.model.type.ElementType; 037import org.ametys.runtime.plugin.component.AbstractLogEnabled; 038import org.ametys.runtime.plugin.component.PluginAware; 039 040/** 041 * Default implementation for {@link ActivityType} storing activity in JCR 042 */ 043public class DefaultActivityType extends AbstractLogEnabled implements Configurable, ActivityType, Serviceable, PluginAware 044{ 045 /** The default time range to merge compatible activities : 5 minutes */ 046 private static final int __MAX_MERGE_ACTIVITY_RANGE = 5 * 60; 047 048 /** The current user provider */ 049 protected CurrentUserProvider _currentUserProvider; 050 /** Helper to get users */ 051 protected UserHelper _userHelper; 052 053 private Map<String, I18nizableText> _supportedEventTypes; 054 055 private String _pluginName; 056 057 public void setPluginInfo(String pluginName, String featureName, String id) 058 { 059 _pluginName = pluginName; 060 } 061 062 @Override 063 public void configure(Configuration configuration) throws ConfigurationException 064 { 065 _supportedEventTypes = new HashMap<>(); 066 067 Configuration[] supportedTypesConf = configuration.getChild("supported-types").getChildren("supported-type"); 068 if (supportedTypesConf.length == 0) 069 { 070 throw new ConfigurationException("Missing 'supported-types' configuration", configuration); 071 } 072 073 for (Configuration conf : supportedTypesConf) 074 { 075 String id = conf.getAttribute("id"); 076 I18nizableText label = I18nizableText.parseI18nizableText(conf.getChild("label"), "plugin." + _pluginName, id); 077 _supportedEventTypes.put(id, label); 078 } 079 } 080 081 @Override 082 public void service(ServiceManager serviceManager) throws ServiceException 083 { 084 _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 085 _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE); 086 } 087 088 @Override 089 public Map<String, I18nizableText> getSupportedEventTypes() 090 { 091 return _supportedEventTypes; 092 } 093 094 @Override 095 public boolean isMergeable (Activity activity1, Activity activity2) 096 { 097 if (activity1 == null || activity2 == null) 098 { 099 return false; 100 } 101 102 String type1 = activity1.getEventType(); 103 UserIdentity author1 = activity1.getAuthor(); 104 ZonedDateTime date1 = activity1.getDate(); 105 106 if (type1 == null || author1 == null || date1 == null) 107 { 108 getLogger().error("Mandatory type, date or author is missing for activity '{}'. Consider it as no mergeable", activity1.getId()); 109 return false; 110 } 111 112 String type2 = activity2.getEventType(); 113 UserIdentity author2 = activity2.getAuthor(); 114 ZonedDateTime date2 = activity2.getDate(); 115 116 if (type2 == null || author2 == null || date2 == null) 117 { 118 getLogger().error("Mandatory type, date or author is missing for activity '{}'. Consider it as no mergeable", activity2.getId()); 119 return false; 120 } 121 122 return type1.equals(type2) && author1.equals(author2) && Math.abs(ChronoUnit.SECONDS.between(date1, date2)) < __MAX_MERGE_ACTIVITY_RANGE; 123 } 124 125 @Override 126 public Map<String, Object> mergeActivities(List<Activity> activities) 127 { 128 Map<String, Object> mergedActivity = activities.get(0).toJSONForClient(); 129 130 mergedActivity.put("amount", activities.size()); 131 132 // Date range 133 if (activities.size() > 1) 134 { 135 ZonedDateTime startDate = null; 136 ZonedDateTime endDate = null; 137 138 for (Activity activity : activities) 139 { 140 ZonedDateTime activityDate = activity.getDate(); 141 if (startDate == null || activityDate.isBefore(startDate)) 142 { 143 startDate = activityDate; 144 } 145 146 if (endDate == null || activityDate.isAfter(endDate)) 147 { 148 endDate = activityDate; 149 } 150 } 151 DataContext dataContext = DataContext.newInstance(); 152 ElementType dateElementType = (ElementType) activities.get(0).getType(ActivityFactory.DATE); 153 mergedActivity.put(ActivityFactory.DATE, dateElementType.valueToJSONForClient(startDate, dataContext)); 154 155 if (endDate != null 156 && startDate != null 157 && ChronoUnit.SECONDS.between(startDate, endDate) > 1) 158 { 159 // Ignore end date if events are less than 1 minute apart 160 mergedActivity.put("endDate", dateElementType.valueToJSONForClient(endDate, dataContext)); 161 } 162 } 163 164 return mergedActivity; 165 } 166}