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