001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.file.collectors; 029 030import org.opencms.db.CmsSubscriptionFilter; 031import org.opencms.db.CmsSubscriptionReadMode; 032import org.opencms.db.CmsVisitedByFilter; 033import org.opencms.file.CmsDataAccessException; 034import org.opencms.file.CmsGroup; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsResource; 037import org.opencms.file.CmsUser; 038import org.opencms.file.I_CmsResource; 039import org.opencms.file.history.I_CmsHistoryResource; 040import org.opencms.main.CmsException; 041import org.opencms.main.CmsLog; 042import org.opencms.main.OpenCms; 043import org.opencms.util.CmsStringUtil; 044 045import java.util.ArrayList; 046import java.util.Arrays; 047import java.util.Collections; 048import java.util.Iterator; 049import java.util.List; 050import java.util.Map; 051 052import org.apache.commons.logging.Log; 053 054/** 055 * A collector that returns visited or subscribed resources depending on the current user and parameters.<p> 056 * 057 * The configuration of the collectors can be done in the parameter String using key value pairs, 058 * separated by the <code>|</code> (pipe) symbol. The following configuration options are available:<p> 059 * <ul> 060 * <li><i>currentuser</i>: determines if the current user should be used to read visited or subscribed resources 061 * (not considered if the <code>user</code> parameter is used)</li> 062 * <li><i>daysfrom</i>: the number of days subtracted from the current day specifying the start point in time from which a resource was visited</li> 063 * <li><i>daysto</i>: the number of days subtracted from the current day specifying the end point in time to which a resource was visited</li> 064 * <li><i>groups</i>: the users group names, separated by comma, to read subscribed resources for</li> 065 * <li><i>includegroups</i>: the include groups flag to read subscribed resources also for the given or current users groups 066 * (not considered if the <code>groups</code> parameter is used)</li> 067 * <li><i>includesubfolders</i>: the include subfolders flag to read subscribed resources also for the subfolders of the given parent folder 068 * (not considered if the <code>resource</code> parameter is not used)</li> 069 * <li><i>mode</i>: the subscription read mode, can be <code>all</code>, <code>visited</code> or <code>unvisited</code> (default)</li> 070 * <li><i>resource</i>: the resource, i.e. the parent folder from which the subscribed or visited resources should be read from</li> 071 * <li><i>user</i>:<the user to read subscribed or visited resources for/li> 072 * </ul> 073 * 074 * Example parameter String that can be used for the collector:<br/> 075 * <code>currentuser=true|daysfrom=14|includegroups=true|mode=unvisited|resource=/demo_en/|includesubfolders=true</code><p> 076 * 077 * @since 8.0 078 */ 079public class CmsSubscriptionCollector extends A_CmsResourceCollector { 080 081 /** The collector parameter key for the current user flag (to set the user in the filters to the current user). */ 082 public static final String PARAM_KEY_CURRENTUSER = "currentuser"; 083 084 /** 085 * The collector parameter key for the number of days subtracted from the current day 086 * specifying the start point in time from which a resource was visited. 087 */ 088 public static final String PARAM_KEY_DAYSFROM = "daysfrom"; 089 090 /** 091 * The collector parameter key for the number of days subtracted from the current day 092 * specifying the end point in time to which a resource was visited.<p> 093 * If the parameter {@link #PARAM_KEY_DAYSFROM} is also used, the value of this key should be less than the value 094 * set as {@link #PARAM_KEY_DAYSFROM} parameter. 095 */ 096 public static final String PARAM_KEY_DAYSTO = "daysto"; 097 098 /** The collector parameter key for the users group names, separated by comma, to read subscribed resources for. */ 099 public static final String PARAM_KEY_GROUPS = "groups"; 100 101 /** The collector parameter key for the include groups flag to read subscribed resources also for the given or current users groups. */ 102 public static final String PARAM_KEY_INCLUDEGROUPS = "includegroups"; 103 104 /** The collector parameter key for the include subfolders flag to read subscribed resources also for the subfolders of the given parent folder. */ 105 public static final String PARAM_KEY_INCLUDESUBFOLDERS = "includesubfolders"; 106 107 /** The collector parameter key for the subscription read mode. */ 108 public static final String PARAM_KEY_MODE = "mode"; 109 110 /** The collector parameter key for the resource, i.e. the parent folder from which the subscribed or visited resources should be read from. */ 111 public static final String PARAM_KEY_RESOURCE = "resource"; 112 113 /** The collector parameter key for the user to read subscribed or visited resources for. */ 114 public static final String PARAM_KEY_USER = "user"; 115 116 /** Static array of the collectors implemented by this class. */ 117 private static final String[] COLLECTORS = {"allVisited", "allSubscribed", "allSubscribedDeleted"}; 118 119 /** Array list for fast collector name lookup. */ 120 private static final List<String> COLLECTORS_LIST = Collections.unmodifiableList(Arrays.asList(COLLECTORS)); 121 122 /** The log object for this class. */ 123 private static final Log LOG = CmsLog.getLog(CmsSubscriptionCollector.class); 124 125 /** 126 * @see org.opencms.file.collectors.I_CmsResourceCollector#getCollectorNames() 127 */ 128 public List<String> getCollectorNames() { 129 130 return COLLECTORS_LIST; 131 } 132 133 /** 134 * @see org.opencms.file.collectors.I_CmsResourceCollector#getCreateLink(org.opencms.file.CmsObject, java.lang.String, java.lang.String) 135 */ 136 public String getCreateLink(CmsObject cms, String collectorName, String param) { 137 138 // this collector does not support creation of new resources 139 return null; 140 } 141 142 /** 143 * @see org.opencms.file.collectors.I_CmsResourceCollector#getCreateParam(org.opencms.file.CmsObject, java.lang.String, java.lang.String) 144 */ 145 public String getCreateParam(CmsObject cms, String collectorName, String param) { 146 147 // this collector does not support creation of new resources 148 return null; 149 } 150 151 /** 152 * @see org.opencms.file.collectors.I_CmsResourceCollector#getResults(org.opencms.file.CmsObject, java.lang.String, java.lang.String) 153 */ 154 public List<CmsResource> getResults(CmsObject cms, String collectorName, String param) 155 throws CmsDataAccessException, CmsException { 156 157 return getResults(cms, collectorName, param, -1); 158 159 } 160 161 /** 162 * @see org.opencms.file.collectors.I_CmsResourceCollector#getResults(org.opencms.file.CmsObject, java.lang.String, java.lang.String) 163 */ 164 public List<CmsResource> getResults(CmsObject cms, String collectorName, String param, int numResults) 165 throws CmsDataAccessException, CmsException { 166 167 // if action is not set use default 168 if (collectorName == null) { 169 collectorName = COLLECTORS[0]; 170 } 171 172 switch (COLLECTORS_LIST.indexOf(collectorName)) { 173 case 0: 174 // "allVisited" 175 return getVisitedResources(cms, param, numResults); 176 case 1: 177 // "allSubscribed" 178 return getSubscribedResources(cms, param, numResults); 179 case 2: 180 // "allSubscribedDeleted" 181 return getSubscribedDeletedResources(cms, param, numResults); 182 default: 183 throw new CmsDataAccessException( 184 Messages.get().container(Messages.ERR_COLLECTOR_NAME_INVALID_1, collectorName)); 185 } 186 } 187 188 /** 189 * Returns the subscribed deleted resources according to the collector parameter.<p> 190 * 191 * @param cms the current users context 192 * @param param an optional collector parameter 193 * @param numResults the number of results 194 * 195 * @return the subscribed deleted resources according to the collector parameter 196 * 197 * @throws CmsException if something goes wrong 198 */ 199 protected List<CmsResource> getSubscribedDeletedResources(CmsObject cms, String param, int numResults) 200 throws CmsException { 201 202 Map<String, String> params = getParameters(param); 203 CmsSubscriptionFilter filter = getSubscriptionFilter(cms, params); 204 String parentPath = filter.getParentPath(); 205 if (CmsStringUtil.isNotEmpty(parentPath)) { 206 parentPath = cms.getRequestContext().removeSiteRoot(parentPath); 207 } 208 209 List<I_CmsHistoryResource> deletedResources = OpenCms.getSubscriptionManager().readSubscribedDeletedResources( 210 cms, 211 filter.getUser(), 212 Boolean.valueOf(params.get(PARAM_KEY_INCLUDEGROUPS)).booleanValue(), 213 parentPath, 214 filter.isIncludeSubFolders(), 215 filter.getFromDate()); 216 217 // cast the history resources to CmsResource objects 218 List<CmsResource> result = new ArrayList<CmsResource>(deletedResources.size()); 219 for (Iterator<I_CmsHistoryResource> i = deletedResources.iterator(); i.hasNext();) { 220 I_CmsHistoryResource deletedResource = i.next(); 221 result.add((CmsResource)deletedResource); 222 } 223 if (numResults > 0) { 224 result = shrinkToFit(result, numResults); 225 } 226 return result; 227 } 228 229 /** 230 * Returns the subscribed resources according to the collector parameter.<p> 231 * 232 * @param cms the current users context 233 * @param param an optional collector parameter 234 * @param numResults the number of results 235 * 236 * @return the subscribed resources according to the collector parameter 237 * 238 * @throws CmsException if something goes wrong 239 */ 240 protected List<CmsResource> getSubscribedResources(CmsObject cms, String param, int numResults) 241 throws CmsException { 242 243 List<CmsResource> result = OpenCms.getSubscriptionManager().readSubscribedResources( 244 cms, 245 getSubscriptionFilter(cms, param)); 246 Collections.sort(result, I_CmsResource.COMPARE_DATE_LAST_MODIFIED); 247 if (numResults > 0) { 248 result = shrinkToFit(result, numResults); 249 } 250 return result; 251 } 252 253 /** 254 * Returns the configured subscription filter to use.<p> 255 * 256 * @param cms the current users context 257 * @param params the optional collector parameters 258 * 259 * @return the configured subscription filter to use 260 * 261 * @throws CmsException if something goes wrong 262 */ 263 protected CmsSubscriptionFilter getSubscriptionFilter(CmsObject cms, Map<String, String> params) 264 throws CmsException { 265 266 CmsSubscriptionFilter filter = new CmsSubscriptionFilter(); 267 268 // initialize the filter 269 initVisitedByFilter(filter, cms, params, false); 270 271 // set subscription filter specific parameters 272 273 // determine the mode to read subscribed resources 274 if (params.containsKey(PARAM_KEY_MODE)) { 275 String modeName = params.get(PARAM_KEY_MODE); 276 filter.setMode(CmsSubscriptionReadMode.modeForName(modeName)); 277 } 278 279 // determine the groups to set in the filter 280 if (params.containsKey(PARAM_KEY_GROUPS)) { 281 List<String> groupNames = CmsStringUtil.splitAsList(params.get(PARAM_KEY_GROUPS), ',', true); 282 for (Iterator<String> i = groupNames.iterator(); i.hasNext();) { 283 String groupName = i.next(); 284 try { 285 CmsGroup group = cms.readGroup(groupName); 286 filter.addGroup(group); 287 } catch (CmsException e) { 288 // error reading a group 289 LOG.error( 290 Messages.get().getBundle().key( 291 Messages.ERR_COLLECTOR_PARAM_INVALID_1, 292 PARAM_KEY_GROUPS + "=" + params.get(PARAM_KEY_GROUPS))); 293 throw e; 294 } 295 } 296 } 297 boolean includeUserGroups = Boolean.valueOf(params.get(PARAM_KEY_INCLUDEGROUPS)).booleanValue(); 298 if (filter.getGroups().isEmpty() && includeUserGroups) { 299 // include the given or current users groups 300 String userName = null; 301 if (filter.getUser() != null) { 302 userName = filter.getUser().getName(); 303 } else { 304 userName = cms.getRequestContext().getCurrentUser().getName(); 305 } 306 filter.setGroups(cms.getGroupsOfUser(userName, false)); 307 } 308 309 return filter; 310 } 311 312 /** 313 * Returns the configured subscription filter to use.<p> 314 * 315 * @param cms the current users context 316 * @param param an optional collector parameter 317 * 318 * @return the configured subscription filter to use 319 * 320 * @throws CmsException if something goes wrong 321 */ 322 protected CmsSubscriptionFilter getSubscriptionFilter(CmsObject cms, String param) throws CmsException { 323 324 return getSubscriptionFilter(cms, getParameters(param)); 325 } 326 327 /** 328 * Returns the configured visited by filter to use.<p> 329 * 330 * @param cms the current users context 331 * @param param an optional collector parameter 332 * 333 * @return the configured visited by filter to use 334 * 335 * @throws CmsException if something goes wrong 336 */ 337 protected CmsVisitedByFilter getVisitedByFilter(CmsObject cms, String param) throws CmsException { 338 339 CmsVisitedByFilter filter = new CmsVisitedByFilter(); 340 Map<String, String> params = getParameters(param); 341 342 // initialize the filter 343 initVisitedByFilter(filter, cms, params, true); 344 345 return filter; 346 } 347 348 /** 349 * Returns the visited resources according to the collector parameter.<p> 350 * 351 * @param cms the current users context 352 * @param param an optional collector parameter 353 * @param numResults the number of results 354 * 355 * @return the visited resources according to the collector parameter 356 * 357 * @throws CmsException if something goes wrong 358 */ 359 protected List<CmsResource> getVisitedResources(CmsObject cms, String param, int numResults) throws CmsException { 360 361 List<CmsResource> result = OpenCms.getSubscriptionManager().readResourcesVisitedBy( 362 cms, 363 getVisitedByFilter(cms, param)); 364 Collections.sort(result, I_CmsResource.COMPARE_DATE_LAST_MODIFIED); 365 if (numResults > 0) { 366 result = shrinkToFit(result, numResults); 367 } 368 return result; 369 } 370 371 /** 372 * Returns the calculated time with the days delta using the base time.<p> 373 * 374 * @param baseTime the base time to calculate the returned time from 375 * @param deltaDays the number of days which should be subtracted from the base time 376 * @param key the parameter key name used for error messages 377 * @param defaultTime the default time is used if there were errors calculating the resulting time 378 * 379 * @return the calculated time 380 */ 381 private long getCalculatedTime(long baseTime, String deltaDays, String key, long defaultTime) { 382 383 try { 384 long days = Long.parseLong(deltaDays); 385 long delta = 1000L * 60L * 60L * 24L * days; 386 long result = baseTime - delta; 387 if (result >= 0) { 388 // result is a valid time stamp 389 return result; 390 } 391 } catch (NumberFormatException e) { 392 LOG.error(Messages.get().getBundle().key(Messages.ERR_COLLECTOR_PARAM_INVALID_1, key + "=" + deltaDays)); 393 } 394 return defaultTime; 395 } 396 397 /** 398 * Returns the collector parameters.<p> 399 * 400 * @param param the collector parameter 401 * 402 * @return the collector parameters 403 */ 404 private Map<String, String> getParameters(String param) { 405 406 if (CmsStringUtil.isNotEmpty(param)) { 407 return CmsStringUtil.splitAsMap(param, "|", "="); 408 } 409 return Collections.emptyMap(); 410 } 411 412 /** 413 * Initializes the visited by filter from the parameters.<p> 414 * 415 * @param filter the filter to initialize 416 * @param cms the current users context 417 * @param params the collector parameters to configure the filter 418 * @param forceSetUser flag to determine if a user has to be set in the filter 419 * (should be <code>true</code> for the visited by filter, <code>false</code> for the subscription filter) 420 * 421 * @throws CmsException if something goes wrong 422 */ 423 private void initVisitedByFilter( 424 CmsVisitedByFilter filter, 425 CmsObject cms, 426 Map<String, String> params, 427 boolean forceSetUser) throws CmsException { 428 429 // determine the user to set in the filter 430 if (params.containsKey(PARAM_KEY_USER)) { 431 try { 432 CmsUser user = cms.readUser(params.get(PARAM_KEY_USER)); 433 filter.setUser(user); 434 } catch (CmsException e) { 435 LOG.error( 436 Messages.get().getBundle().key(Messages.ERR_COLLECTOR_PARAM_USER_1, params.get(PARAM_KEY_USER))); 437 throw e; 438 } 439 } 440 boolean setCurrentUser = Boolean.valueOf(params.get(PARAM_KEY_CURRENTUSER)).booleanValue(); 441 if ((filter.getUser() == null) && (forceSetUser || setCurrentUser)) { 442 // set current user 443 filter.setUser(cms.getRequestContext().getCurrentUser()); 444 } 445 446 // determine the time stamps to set in the filter 447 long currentTime = System.currentTimeMillis(); 448 if (params.containsKey(PARAM_KEY_DAYSFROM)) { 449 filter.setFromDate(getCalculatedTime(currentTime, params.get(PARAM_KEY_DAYSFROM), PARAM_KEY_DAYSFROM, 0L)); 450 } 451 if (params.containsKey(PARAM_KEY_DAYSTO)) { 452 filter.setToDate( 453 getCalculatedTime(currentTime, params.get(PARAM_KEY_DAYSTO), PARAM_KEY_DAYSTO, Long.MAX_VALUE)); 454 } 455 456 // determine if a parent folder should be used 457 if (params.containsKey(PARAM_KEY_RESOURCE)) { 458 try { 459 CmsResource resource = cms.readResource(params.get(PARAM_KEY_RESOURCE)); 460 filter.setParentResource(resource); 461 // check if the sub folders should be included 462 if (params.containsKey(PARAM_KEY_INCLUDESUBFOLDERS)) { 463 boolean includeSubFolders = Boolean.valueOf(params.get(PARAM_KEY_INCLUDESUBFOLDERS)).booleanValue(); 464 filter.setIncludeSubfolders(includeSubFolders); 465 } 466 } catch (CmsException e) { 467 LOG.error( 468 Messages.get().getBundle().key( 469 Messages.ERR_COLLECTOR_PARAM_INVALID_1, 470 PARAM_KEY_RESOURCE + "=" + params.get(PARAM_KEY_RESOURCE))); 471 throw e; 472 } 473 } 474 } 475 476}