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