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.workflow; 029 030import org.opencms.ade.publish.CmsPublishRelationFinder.ResourceMap; 031import org.opencms.ade.publish.Messages; 032import org.opencms.ade.publish.shared.CmsPublishOptions; 033import org.opencms.ade.publish.shared.CmsPublishResource; 034import org.opencms.ade.publish.shared.CmsPublishResourceInfo; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsProject; 037import org.opencms.file.CmsResource; 038import org.opencms.file.CmsResourceFilter; 039import org.opencms.file.CmsUser; 040import org.opencms.gwt.CmsIconUtil; 041import org.opencms.gwt.CmsVfsService; 042import org.opencms.gwt.shared.CmsPermissionInfo; 043import org.opencms.lock.CmsLock; 044import org.opencms.lock.CmsLockFilter; 045import org.opencms.main.CmsException; 046import org.opencms.main.CmsLog; 047import org.opencms.main.OpenCms; 048import org.opencms.security.CmsOrganizationalUnit; 049import org.opencms.security.CmsPermissionSet; 050import org.opencms.ui.components.CmsResourceIcon; 051import org.opencms.util.CmsStringUtil; 052import org.opencms.util.CmsUUID; 053import org.opencms.workplace.explorer.CmsResourceUtil; 054 055import java.util.ArrayList; 056import java.util.Arrays; 057import java.util.Collections; 058import java.util.Comparator; 059import java.util.Date; 060import java.util.HashMap; 061import java.util.HashSet; 062import java.util.List; 063import java.util.Locale; 064import java.util.Map; 065import java.util.Set; 066 067import org.apache.commons.logging.Log; 068 069import com.google.common.base.Predicate; 070import com.google.common.collect.ComparisonChain; 071import com.google.common.collect.Lists; 072import com.google.common.collect.Maps; 073 074/** 075 * Default formatter class for publish resources.<p> 076 */ 077public class CmsDefaultPublishResourceFormatter implements I_CmsPublishResourceFormatter { 078 079 /** 080 * Excludes resources which have already been published.<p> 081 */ 082 public class AlreadyPublishedValidator implements I_PublishResourceValidator { 083 084 /** 085 * @see org.opencms.workflow.CmsDefaultPublishResourceFormatter.I_PublishResourceValidator#findInvalidResources(java.util.Set) 086 */ 087 public Set<CmsResource> findInvalidResources(Set<CmsResource> resources) { 088 089 Set<CmsResource> result = new HashSet<CmsResource>(); 090 for (CmsResource resource : resources) { 091 if (resource.getState().isUnchanged()) { 092 result.add(resource); 093 } 094 } 095 return result; 096 } 097 098 /** 099 * @see org.opencms.workflow.CmsDefaultPublishResourceFormatter.I_PublishResourceValidator#getInfoForResource(org.opencms.file.CmsResource) 100 */ 101 public CmsPublishResourceInfo getInfoForResource(CmsResource resource) throws CmsException { 102 103 String info; 104 CmsPublishResourceInfo.Type infoType; 105 CmsPublishResourceInfo infoObj; 106 String publishUser = getOuAwareName(m_cms, m_cms.readUser(resource.getUserLastModified()).getName()); 107 Date publishDate = new Date(resource.getDateLastModified()); 108 info = Messages.get().getBundle(getLocale()).key( 109 Messages.GUI_RESOURCE_PUBLISHED_BY_2, 110 publishUser, 111 publishDate); 112 infoType = CmsPublishResourceInfo.Type.PUBLISHED; 113 infoObj = new CmsPublishResourceInfo(info, infoType); 114 return infoObj; 115 } 116 } 117 118 /** 119 * Validator which checks if resources are locked by someone else.<p> 120 */ 121 public class BlockingLockedValidator implements I_PublishResourceValidator { 122 123 /** 124 * @see org.opencms.workflow.CmsDefaultPublishResourceFormatter.I_PublishResourceValidator#findInvalidResources(java.util.Set) 125 */ 126 @SuppressWarnings("synthetic-access") 127 public Set<CmsResource> findInvalidResources(Set<CmsResource> resources) { 128 129 CmsUser user = m_cms.getRequestContext().getCurrentUser(); 130 CmsLockFilter blockingFilter = CmsLockFilter.FILTER_ALL; 131 blockingFilter = blockingFilter.filterNotLockableByUser(user); 132 Set<CmsResource> result = new HashSet<CmsResource>(); 133 134 for (CmsResource resource : resources) { 135 try { 136 List<CmsResource> blockingLocked = m_cms.getLockedResourcesWithCache( 137 resource, 138 blockingFilter, 139 m_lockedResourceCache); 140 for (CmsResource res : blockingLocked) { 141 result.add(res); 142 } 143 } catch (Exception e) { 144 // error reading the resource list, should usually never happen 145 if (LOG.isErrorEnabled()) { 146 LOG.error(e.getLocalizedMessage(), e); 147 } 148 } 149 } 150 return result; 151 } 152 153 /** 154 * @see org.opencms.workflow.CmsDefaultPublishResourceFormatter.I_PublishResourceValidator#getInfoForResource(org.opencms.file.CmsResource) 155 */ 156 public CmsPublishResourceInfo getInfoForResource(CmsResource resource) throws CmsException { 157 158 String info; 159 CmsPublishResourceInfo.Type infoType; 160 CmsPublishResourceInfo infoObj; 161 CmsLock lock = m_cms.getLock(resource); 162 info = Messages.get().getBundle(getLocale()).key( 163 Messages.GUI_RESOURCE_LOCKED_BY_2, 164 getOuAwareName(m_cms, m_cms.readUser(lock.getUserId()).getName()), 165 getOuAwareName(m_cms, lock.getProject().getName())); 166 infoType = CmsPublishResourceInfo.Type.LOCKED; 167 infoObj = new CmsPublishResourceInfo(info, infoType); 168 return infoObj; 169 } 170 } 171 172 /** 173 * Compares publish resources by their sort date.<p> 174 */ 175 public static class DefaultComparator implements Comparator<CmsPublishResource> { 176 177 /** 178 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 179 */ 180 public int compare(CmsPublishResource first, CmsPublishResource second) { 181 182 return ComparisonChain.start().compare(-first.getSortDate(), -second.getSortDate()).result(); 183 } 184 } 185 186 /** 187 * Validator which can exclude some resources from publishing and supplies a status object for the excluded resources.<p> 188 */ 189 public static interface I_PublishResourceValidator { 190 191 /** 192 * Finds the resources which should be excluded.<p> 193 * 194 * @param input the set of input resources 195 * 196 * @return the excluded resources 197 */ 198 Set<CmsResource> findInvalidResources(Set<CmsResource> input); 199 200 /** 201 * Gets the status information for an excluded resource.<p> 202 * 203 * @param resource the resource for which to get the status 204 * @return the status for the resource 205 * @throws CmsException if something goes wrong 206 */ 207 CmsPublishResourceInfo getInfoForResource(CmsResource resource) throws CmsException; 208 } 209 210 /** 211 * Validator which excludes resources for which the user has no publish permissions.<p> 212 */ 213 public class NoPermissionsValidator implements I_PublishResourceValidator { 214 215 /** 216 * @see org.opencms.workflow.CmsDefaultPublishResourceFormatter.I_PublishResourceValidator#findInvalidResources(java.util.Set) 217 */ 218 @SuppressWarnings("synthetic-access") 219 public Set<CmsResource> findInvalidResources(Set<CmsResource> resources) { 220 221 Set<CmsResource> result = new HashSet<CmsResource>(); 222 Set<CmsUUID> projectIds = new HashSet<CmsUUID>(); 223 try { 224 for (CmsProject project : OpenCms.getOrgUnitManager().getAllManageableProjects(m_cms, "", true)) { 225 projectIds.add(project.getUuid()); 226 } 227 } catch (CmsException e) { 228 // should never happen 229 LOG.error(e.getLocalizedMessage(), e); 230 } 231 for (CmsResource resource : resources) { 232 try { 233 if (!projectIds.contains(resource.getProjectLastModified()) 234 && !m_cms.hasPermissions(resource, CmsPermissionSet.ACCESS_DIRECT_PUBLISH)) { 235 result.add(resource); 236 } 237 } catch (Exception e) { 238 // error reading the permissions, should usually never happen 239 if (LOG.isErrorEnabled()) { 240 LOG.error(e.getLocalizedMessage(), e); 241 } 242 } 243 } 244 return result; 245 } 246 247 /** 248 * @see org.opencms.workflow.CmsDefaultPublishResourceFormatter.I_PublishResourceValidator#getInfoForResource(org.opencms.file.CmsResource) 249 */ 250 public CmsPublishResourceInfo getInfoForResource(CmsResource resource) { 251 252 String info; 253 CmsPublishResourceInfo.Type infoType; 254 CmsPublishResourceInfo infoObj; 255 info = Messages.get().getBundle(getLocale()).key(Messages.GUI_RESOURCE_NOT_ENOUGH_PERMISSIONS_0); 256 infoType = CmsPublishResourceInfo.Type.PERMISSIONS; 257 infoObj = new CmsPublishResourceInfo(info, infoType); 258 return infoObj; 259 } 260 } 261 262 /** 263 * Predicate which checks whether the current user has publish permissions for a resource.<p> 264 */ 265 public class PublishPermissionFilter implements Predicate<CmsResource> { 266 267 /** 268 * @see com.google.common.base.Predicate#apply(java.lang.Object) 269 */ 270 @SuppressWarnings("synthetic-access") 271 public boolean apply(CmsResource input) { 272 273 try { 274 return m_cms.hasPermissions( 275 input, 276 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 277 false, 278 CmsResourceFilter.ALL); 279 } catch (CmsException e) { 280 LOG.error(e.getLocalizedMessage(), e); 281 return true; 282 } 283 } 284 285 } 286 287 /** The logger for this class. */ 288 private static final Log LOG = CmsLog.getLog(CmsDefaultPublishResourceFormatter.class); 289 290 /** The publish options. */ 291 protected CmsPublishOptions m_options; 292 293 /** The CMS context for this class. */ 294 CmsObject m_cms; 295 296 /** Cache for locked resources. */ 297 private Map<String, CmsResource> m_lockedResourceCache = new HashMap<String, CmsResource>(); 298 299 /** The publish resources. */ 300 private List<CmsPublishResource> m_publishResources; 301 302 /** The publish resources by id. */ 303 private Map<CmsUUID, CmsResource> m_resources = new HashMap<CmsUUID, CmsResource>(); 304 305 /** 306 * Constructor.<p> 307 * 308 * 309 * @param cms the CMS context to use 310 */ 311 public CmsDefaultPublishResourceFormatter(CmsObject cms) { 312 313 m_cms = cms; 314 } 315 316 /** 317 * Returns the simple name if the ou is the same as the current user's ou.<p> 318 * 319 * @param cms the CMS context 320 * @param name the fully qualified name to check 321 * 322 * @return the simple name if the ou is the same as the current user's ou 323 */ 324 public static String getOuAwareName(CmsObject cms, String name) { 325 326 String ou = CmsOrganizationalUnit.getParentFqn(name); 327 if (ou.equals(cms.getRequestContext().getCurrentUser().getOuFqn())) { 328 return CmsOrganizationalUnit.getSimpleName(name); 329 } 330 return CmsOrganizationalUnit.SEPARATOR + name; 331 } 332 333 /** 334 * @see org.opencms.workflow.I_CmsPublishResourceFormatter#getPublishResources() 335 */ 336 public List<CmsPublishResource> getPublishResources() { 337 338 sortResult(m_publishResources); 339 return m_publishResources; 340 } 341 342 /** 343 * @see org.opencms.workflow.I_CmsPublishResourceFormatter#initialize(org.opencms.ade.publish.shared.CmsPublishOptions, org.opencms.ade.publish.CmsPublishRelationFinder.ResourceMap) 344 */ 345 public void initialize(CmsPublishOptions options, ResourceMap resources) throws CmsException { 346 347 m_options = options; 348 Predicate<CmsResource> resourceMapFilter = getResourceMapFilter(); 349 if (resourceMapFilter != null) { 350 resources = resources.filter(resourceMapFilter); 351 } 352 for (CmsResource parentRes : resources.keySet()) { 353 m_resources.put(parentRes.getStructureId(), parentRes); 354 for (CmsResource childRes : resources.get(parentRes)) { 355 m_resources.put(childRes.getStructureId(), childRes); 356 } 357 } 358 Map<CmsUUID, CmsPublishResourceInfo> warnings = computeWarnings(); 359 m_publishResources = Lists.newArrayList(); 360 for (CmsResource parentRes : resources.keySet()) { 361 CmsPublishResource parentPubRes = createPublishResource(parentRes); 362 parentPubRes.setInfo(warnings.get(parentRes.getStructureId())); 363 for (CmsResource childRes : resources.get(parentRes)) { 364 CmsPublishResource childPubRes = createPublishResource(childRes); 365 childPubRes.setInfo(warnings.get(childRes.getStructureId())); 366 parentPubRes.getRelated().add(childPubRes); 367 } 368 if ((m_options.getProjectId() == null) || m_options.getProjectId().isNullUUID()) { 369 parentPubRes.setRemovable(true); 370 } 371 m_publishResources.add(parentPubRes); 372 } 373 } 374 375 /** 376 * Creates the publish resource warnings.<p> 377 * 378 * @return a map from structure ids to the warnings for the corresponding resources 379 */ 380 protected Map<CmsUUID, CmsPublishResourceInfo> computeWarnings() { 381 382 Map<CmsUUID, CmsPublishResourceInfo> warnings = Maps.newHashMap(); 383 Set<CmsResource> resourcesWithoutErrors = new HashSet<CmsResource>(m_resources.values()); 384 385 List<I_PublishResourceValidator> validators = getValidators(); 386 List<Set<CmsResource>> excludedSetsForValidators = Lists.newArrayList(); 387 for (int i = 0; i < validators.size(); i++) { 388 I_PublishResourceValidator validator = validators.get(i); 389 Set<CmsResource> excluded = validator.findInvalidResources(resourcesWithoutErrors); 390 resourcesWithoutErrors.removeAll(excluded); 391 excludedSetsForValidators.add(excluded); 392 } 393 for (CmsResource resource : m_resources.values()) { 394 CmsPublishResourceInfo info = null; 395 try { 396 for (int i = 0; i < validators.size(); i++) { 397 if (excludedSetsForValidators.get(i).contains(resource)) { 398 info = validators.get(i).getInfoForResource(resource); 399 break; 400 } 401 } 402 } catch (CmsException e) { 403 LOG.error(e.getLocalizedMessage(), e); 404 } 405 warnings.put(resource.getStructureId(), info); 406 } 407 return warnings; 408 } 409 410 /** 411 * Creates a publish resource bean from a resource.<p> 412 * 413 * @param resource the resource 414 * @return the publish resource bean 415 * 416 * @throws CmsException if something goes wrong 417 */ 418 protected CmsPublishResource createPublishResource(CmsResource resource) throws CmsException { 419 420 CmsResourceUtil resUtil = new CmsResourceUtil(m_cms, resource); 421 CmsPermissionInfo permissionInfo = OpenCms.getADEManager().getPermissionInfo(m_cms, resource, null); 422 423 String typeName = CmsIconUtil.getDisplayType(m_cms, resource); 424 String detailTypeName = CmsResourceIcon.getDefaultFileOrDetailType(m_cms, resource); 425 CmsPublishResource pubResource = new CmsPublishResource( 426 resource.getStructureId(), 427 resUtil.getFullPath(), 428 resUtil.getTitle(), 429 typeName, 430 resource.getState(), 431 permissionInfo, 432 resource.getDateLastModified(), 433 resUtil.getUserLastModified(), 434 CmsVfsService.formatDateTime(m_cms, resource.getDateLastModified()), 435 false, 436 null, 437 new ArrayList<CmsPublishResource>()); 438 pubResource.setBigIconClasses(CmsIconUtil.getIconClasses(typeName, resource.getName(), false)); 439 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(detailTypeName)) { 440 pubResource.setSmallIconClasses(CmsIconUtil.getIconClasses(detailTypeName, null, true)); 441 } 442 return pubResource; 443 } 444 445 /** 446 * Gets the workplace locale for the currently used CMS context.<p> 447 * 448 * @return the workplace locale 449 */ 450 protected Locale getLocale() { 451 452 return OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms); 453 } 454 455 /** 456 * Gets the resource map filter.<p> 457 * 458 * This can be used to remove resources which shouldn't be displayed.<p> 459 * 460 * @return a predicate whose 461 */ 462 protected Predicate<CmsResource> getResourceMapFilter() { 463 464 return new PublishPermissionFilter(); 465 } 466 467 /** 468 * Gets the list of publish resource validators.<p> 469 * 470 * @return the list of publish resource validators 471 */ 472 protected List<I_PublishResourceValidator> getValidators() { 473 474 return Arrays.asList( 475 new AlreadyPublishedValidator(), 476 new NoPermissionsValidator(), 477 new BlockingLockedValidator()); 478 } 479 480 /** 481 * Sorts the result publish resource list.<p> 482 * 483 * @param publishResources the list to sort 484 */ 485 protected void sortResult(List<CmsPublishResource> publishResources) { 486 487 Collections.sort(publishResources, new DefaultComparator()); 488 for (CmsPublishResource resource : publishResources) { 489 Collections.sort(resource.getRelated(), new DefaultComparator()); 490 } 491 } 492 493}