001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (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.cmis; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.file.CmsVfsResourceNotFoundException; 034import org.opencms.lock.CmsLock; 035import org.opencms.main.CmsException; 036import org.opencms.main.CmsIllegalArgumentException; 037import org.opencms.security.CmsAccessControlEntry; 038import org.opencms.security.CmsPermissionSet; 039import org.opencms.security.CmsPrincipal; 040import org.opencms.security.CmsRole; 041import org.opencms.security.CmsSecurityException; 042import org.opencms.util.CmsUUID; 043 044import java.math.BigDecimal; 045import java.math.BigInteger; 046import java.util.ArrayList; 047import java.util.GregorianCalendar; 048import java.util.LinkedHashSet; 049import java.util.List; 050import java.util.Set; 051import java.util.TimeZone; 052 053import org.apache.chemistry.opencmis.commons.PropertyIds; 054import org.apache.chemistry.opencmis.commons.data.Properties; 055import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; 056import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; 057import org.apache.chemistry.opencmis.commons.enums.Action; 058import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException; 059import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; 060import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; 061import org.apache.chemistry.opencmis.commons.exceptions.CmisUnauthorizedException; 062import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl; 063import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl; 064import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl; 065import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalImpl; 066import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlImpl; 067import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl; 068import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl; 069import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl; 070import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl; 071 072/** 073 * Utility class for operations which are frequently used by CMIS service methods.<p> 074 */ 075public final class CmsCmisUtil { 076 077 /** 078 * Private constructor to prevent instantiation.<p> 079 */ 080 private CmsCmisUtil() { /* Prevent instantiation. */ 081 082 } 083 084 /** 085 * Adds an action to a set of actions if a condition is fulfilled.<p> 086 * 087 * @param aas the set of actions 088 * @param action the action to add 089 * @param condition the value of the condition for adding the action 090 */ 091 public static void addAction(Set<Action> aas, Action action, boolean condition) { 092 093 if (condition) { 094 aas.add(action); 095 } 096 } 097 098 /** 099 * Helper method to add the dynamic properties for a resource.<p> 100 * 101 * @param cms the current CMS context 102 * @param typeManager the type manager instance 103 * @param props the properties to which the dynamic properties should be added 104 * @param typeId the type id 105 * @param resource the resource 106 * @param filter the property filter 107 */ 108 public static void addDynamicProperties( 109 CmsObject cms, 110 CmsCmisTypeManager typeManager, 111 PropertiesImpl props, 112 String typeId, 113 CmsResource resource, 114 Set<String> filter) { 115 116 List<I_CmsPropertyProvider> providers = typeManager.getPropertyProviders(); 117 for (I_CmsPropertyProvider provider : providers) { 118 String propertyName = CmsCmisTypeManager.PROPERTY_PREFIX_DYNAMIC + provider.getName(); 119 if (!checkAddProperty(typeManager, props, typeId, filter, propertyName)) { 120 continue; 121 } 122 try { 123 String value = provider.getPropertyValue(cms, resource); 124 addPropertyString(typeManager, props, typeId, filter, propertyName, value); 125 } catch (Throwable t) { 126 addPropertyString(typeManager, props, typeId, filter, propertyName, null); 127 } 128 } 129 } 130 131 /** 132 * Adds bigint property to a PropertiesImpl.<p> 133 * 134 * 135 * @param typeManager the type manager 136 * @param props the properties 137 * @param typeId the type id 138 * @param filter the property filter string 139 * @param id the property id 140 * @param value the property value 141 */ 142 public static void addPropertyBigInteger( 143 CmsCmisTypeManager typeManager, 144 PropertiesImpl props, 145 String typeId, 146 Set<String> filter, 147 String id, 148 BigInteger value) { 149 150 if (!checkAddProperty(typeManager, props, typeId, filter, id)) { 151 return; 152 } 153 154 props.addProperty(new PropertyIntegerImpl(id, value)); 155 } 156 157 /** 158 * Adds a boolean property to a PropertiesImpl.<p> 159 * 160 * @param typeManager 161 * @param props the properties 162 * @param typeId the type id 163 * @param filter the property filter string 164 * @param id the property id 165 * @param value the property value 166 */ 167 public static void addPropertyBoolean( 168 CmsCmisTypeManager typeManager, 169 PropertiesImpl props, 170 String typeId, 171 Set<String> filter, 172 String id, 173 boolean value) { 174 175 if (!checkAddProperty(typeManager, props, typeId, filter, id)) { 176 return; 177 } 178 179 props.addProperty(new PropertyBooleanImpl(id, Boolean.valueOf(value))); 180 } 181 182 /** 183 * Adds a date/time property to a PropertiesImpl.<p> 184 * 185 * @param typeManager the type manager 186 * @param props the properties 187 * @param typeId the type id 188 * @param filter the property filter string 189 * @param id the property id 190 * @param value the property value 191 */ 192 public static void addPropertyDateTime( 193 CmsCmisTypeManager typeManager, 194 PropertiesImpl props, 195 String typeId, 196 Set<String> filter, 197 String id, 198 GregorianCalendar value) { 199 200 if (!checkAddProperty(typeManager, props, typeId, filter, id)) { 201 return; 202 } 203 204 props.addProperty(new PropertyDateTimeImpl(id, value)); 205 } 206 207 /** 208 * Adds the default value of property if defined. 209 * 210 * @param props the Properties object 211 * @param propDef the property definition 212 * 213 * @return true if the property could be added 214 */ 215 @SuppressWarnings("unchecked") 216 public static boolean addPropertyDefault(PropertiesImpl props, PropertyDefinition<?> propDef) { 217 218 if ((props == null) || (props.getProperties() == null)) { 219 throw new IllegalArgumentException("Props must not be null!"); 220 } 221 222 if (propDef == null) { 223 return false; 224 } 225 226 List<?> defaultValue = propDef.getDefaultValue(); 227 if ((defaultValue != null) && (!defaultValue.isEmpty())) { 228 switch (propDef.getPropertyType()) { 229 case BOOLEAN: 230 props.addProperty(new PropertyBooleanImpl(propDef.getId(), (List<Boolean>)defaultValue)); 231 break; 232 case DATETIME: 233 props.addProperty(new PropertyDateTimeImpl(propDef.getId(), (List<GregorianCalendar>)defaultValue)); 234 break; 235 case DECIMAL: 236 props.addProperty(new PropertyDecimalImpl(propDef.getId(), (List<BigDecimal>)defaultValue)); 237 break; 238 case HTML: 239 props.addProperty(new PropertyHtmlImpl(propDef.getId(), (List<String>)defaultValue)); 240 break; 241 case ID: 242 props.addProperty(new PropertyIdImpl(propDef.getId(), (List<String>)defaultValue)); 243 break; 244 case INTEGER: 245 props.addProperty(new PropertyIntegerImpl(propDef.getId(), (List<BigInteger>)defaultValue)); 246 break; 247 case STRING: 248 props.addProperty(new PropertyStringImpl(propDef.getId(), (List<String>)defaultValue)); 249 break; 250 case URI: 251 props.addProperty(new PropertyUriImpl(propDef.getId(), (List<String>)defaultValue)); 252 break; 253 default: 254 throw new RuntimeException("Unknown datatype! Spec change?"); 255 } 256 257 return true; 258 } 259 260 return false; 261 } 262 263 /** 264 * Helper method for adding an id-valued property.<p> 265 * 266 * @param typeManager the type manager 267 * @param props the properties to add to 268 * @param typeId the type id 269 * @param filter the property filter 270 * @param id the property id 271 * @param value the property value 272 */ 273 public static void addPropertyId( 274 CmsCmisTypeManager typeManager, 275 PropertiesImpl props, 276 String typeId, 277 Set<String> filter, 278 String id, 279 String value) { 280 281 if (!checkAddProperty(typeManager, props, typeId, filter, id)) { 282 return; 283 } 284 PropertyIdImpl result = new PropertyIdImpl(id, value); 285 result.setQueryName(id); 286 287 props.addProperty(result); 288 } 289 290 /** 291 * Helper method for adding an id-list-valued property.<p> 292 * 293 * @param typeManager 294 * @param props the properties to add to 295 * @param typeId the type id 296 * @param filter the property filter 297 * @param id the property id 298 * @param value the property value 299 */ 300 public static void addPropertyIdList( 301 CmsCmisTypeManager typeManager, 302 PropertiesImpl props, 303 String typeId, 304 Set<String> filter, 305 String id, 306 List<String> value) { 307 308 if (!checkAddProperty(typeManager, props, typeId, filter, id)) { 309 return; 310 } 311 312 props.addProperty(new PropertyIdImpl(id, value)); 313 } 314 315 /** 316 * Adds an integer property to a PropertiesImpl.<p> 317 * 318 * @param typeManager the type manager 319 * @param props the properties 320 * @param typeId the type id 321 * @param filter the property filter string 322 * @param id the property id 323 * @param value the property value 324 */ 325 public static void addPropertyInteger( 326 CmsCmisTypeManager typeManager, 327 PropertiesImpl props, 328 String typeId, 329 Set<String> filter, 330 String id, 331 long value) { 332 333 addPropertyBigInteger(typeManager, props, typeId, filter, id, BigInteger.valueOf(value)); 334 } 335 336 /** 337 * Adds a string property to a PropertiesImpl.<p> 338 * 339 * @param typeManager 340 * @param props the properties 341 * @param typeId the type id 342 * @param filter the property filter string 343 * @param id the property id 344 * @param value the property value 345 */ 346 public static void addPropertyString( 347 CmsCmisTypeManager typeManager, 348 PropertiesImpl props, 349 String typeId, 350 Set<String> filter, 351 String id, 352 String value) { 353 354 if (!checkAddProperty(typeManager, props, typeId, filter, id)) { 355 return; 356 } 357 PropertyStringImpl result = new PropertyStringImpl(id, value); 358 result.setQueryName(id); 359 props.addProperty(result); 360 } 361 362 /** 363 * Checks whether a property can be added to a Properties. 364 * 365 * @param typeManager 366 * @param properties the properties object 367 * @param typeId the type id 368 * @param filter the property filter 369 * @param id the property id 370 * 371 * @return true if the property should be added 372 */ 373 public static boolean checkAddProperty( 374 CmsCmisTypeManager typeManager, 375 Properties properties, 376 String typeId, 377 Set<String> filter, 378 String id) { 379 380 if ((properties == null) || (properties.getProperties() == null)) { 381 throw new IllegalArgumentException("Properties must not be null!"); 382 } 383 384 if (id == null) { 385 throw new IllegalArgumentException("Id must not be null!"); 386 } 387 388 TypeDefinition type = typeManager.getType(typeId); 389 if (type == null) { 390 throw new IllegalArgumentException("Unknown type: " + typeId); 391 } 392 if (!type.getPropertyDefinitions().containsKey(id)) { 393 throw new IllegalArgumentException("Unknown property: " + id); 394 } 395 396 String queryName = type.getPropertyDefinitions().get(id).getQueryName(); 397 398 if ((queryName != null) && (filter != null)) { 399 if (!filter.contains(queryName)) { 400 return false; 401 } else { 402 filter.remove(queryName); 403 } 404 } 405 406 return true; 407 } 408 409 /** 410 * Checks whether a name is a valid OpenCms resource name and throws an exception otherwise.<p> 411 * 412 * @param name the name to check 413 */ 414 public static void checkResourceName(String name) { 415 416 try { 417 CmsResource.checkResourceName(name); 418 } catch (CmsIllegalArgumentException e) { 419 throw new CmisNameConstraintViolationException(e.getLocalizedMessage(), e); 420 } 421 } 422 423 /** 424 * Tries to lock a resource and throws an exception if it can't be locked.<p> 425 * 426 * Returns true only if the resource wasn't already locked before.<p> 427 * 428 * @param cms the CMS context 429 * @param resource the resource to lock 430 * @return true if the resource wasn't already locked 431 * 432 * @throws CmsException if something goes wrong 433 */ 434 public static boolean ensureLock(CmsObject cms, CmsResource resource) throws CmsException { 435 436 CmsLock lock = cms.getLock(resource); 437 if (lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) { 438 return false; 439 } 440 cms.lockResourceTemporary(resource); 441 return true; 442 } 443 444 /** 445 * Gets a user-readable name for a principal id read from an ACE.<p> 446 * 447 * @param cms the current CMS context 448 * @param principalId the principal id from the ACE 449 * @return the name of the principle 450 */ 451 public static String getAcePrincipalName(CmsObject cms, CmsUUID principalId) { 452 453 if (CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_ID.equals(principalId)) { 454 return CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_NAME; 455 } 456 if (CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_ID.equals(principalId)) { 457 return CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_NAME; 458 } 459 CmsRole role = CmsRole.valueOfId(principalId); 460 if (role != null) { 461 return role.getRoleName(); 462 } 463 try { 464 return CmsPrincipal.readPrincipalIncludingHistory(cms, principalId).getName(); 465 } catch (CmsException e) { 466 return "" + principalId; 467 } 468 } 469 470 /** 471 * Converts an OpenCms ACE to a list of basic CMIS permissions.<p> 472 * 473 * @param ace the access control entry 474 * 475 * @return the list of permissions 476 */ 477 public static List<String> getCmisPermissions(CmsAccessControlEntry ace) { 478 479 int permissionBits = ace.getPermissions().getPermissions(); 480 List<String> result = new ArrayList<String>(); 481 if (0 != (permissionBits & CmsPermissionSet.PERMISSION_READ)) { 482 result.add(A_CmsCmisRepository.CMIS_READ); 483 } 484 if (0 != (permissionBits & CmsPermissionSet.PERMISSION_WRITE)) { 485 result.add(A_CmsCmisRepository.CMIS_WRITE); 486 } 487 int all = CmsPermissionSet.PERMISSION_WRITE 488 | CmsPermissionSet.PERMISSION_READ 489 | CmsPermissionSet.PERMISSION_CONTROL 490 | CmsPermissionSet.PERMISSION_DIRECT_PUBLISH; 491 if ((permissionBits & all) == all) { 492 result.add(A_CmsCmisRepository.CMIS_ALL); 493 } 494 return result; 495 } 496 497 /** 498 * Converts an OpenCms access control entry to a list of CMIS permissions which represent native OpenCms permissions.<p> 499 * 500 * @param ace the access control entry 501 * @return the list of permissions for the entry 502 */ 503 public static List<String> getNativePermissions(CmsAccessControlEntry ace) { 504 505 List<String> result = getNativePermissions(ace.getPermissions().getAllowedPermissions(), false); 506 result.addAll(getNativePermissions(ace.getPermissions().getDeniedPermissions(), true)); 507 return result; 508 } 509 510 /** 511 * Converts an OpenCms access control bitset to a list of CMIS permissions representing native OpenCms permissions.<p> 512 * 513 * @param permissionBits the permission bits 514 * @param denied if the permission bitset refers to a list of denied rather than allowed permissions 515 * 516 * @return the list of native permissions 517 */ 518 public static List<String> getNativePermissions(int permissionBits, boolean denied) { 519 520 List<String> result = new ArrayList<String>(); 521 String prefix = denied ? "opencms:deny-" : "opencms:"; 522 if ((permissionBits & CmsPermissionSet.PERMISSION_READ) != 0) { 523 result.add(prefix + "read"); 524 } 525 if ((permissionBits & CmsPermissionSet.PERMISSION_WRITE) != 0) { 526 result.add(prefix + "write"); 527 } 528 529 if ((permissionBits & CmsPermissionSet.PERMISSION_VIEW) != 0) { 530 result.add(prefix + "view"); 531 } 532 533 if ((permissionBits & CmsPermissionSet.PERMISSION_CONTROL) != 0) { 534 result.add(prefix + "control"); 535 } 536 537 if ((permissionBits & CmsPermissionSet.PERMISSION_DIRECT_PUBLISH) != 0) { 538 result.add(prefix + "publish"); 539 } 540 return result; 541 } 542 543 /** 544 * Wrap OpenCms into OpenCMIS exceptions and rethrow them.<p> 545 * 546 * @param e the exception to handle 547 */ 548 public static void handleCmsException(CmsException e) { 549 550 if (e instanceof CmsVfsResourceNotFoundException) { 551 throw new CmisObjectNotFoundException(e.getLocalizedMessage(), e); 552 } else if (e instanceof CmsSecurityException) { 553 throw new CmisUnauthorizedException(e.getLocalizedMessage(), e); 554 } else { 555 throw new CmisRuntimeException(e.getLocalizedMessage(), e); 556 } 557 } 558 559 /** 560 * Checks whether the given resource has any children.<p> 561 * 562 * @param cms the CMS context 563 * @param resource the resource to check 564 * 565 * @return true if the resource has children 566 * 567 * @throws CmsException if something goes wrong 568 */ 569 public static boolean hasChildren(CmsObject cms, CmsResource resource) throws CmsException { 570 571 return !cms.getResourcesInFolder(cms.getSitePath(resource), CmsResourceFilter.ALL).isEmpty(); 572 } 573 574 /** 575 * Converts milliseconds into a calendar object. 576 * 577 * @param millis a time given in milliseconds after epoch 578 * @return the calendar object for the given time 579 */ 580 public static GregorianCalendar millisToCalendar(long millis) { 581 582 GregorianCalendar result = new GregorianCalendar(); 583 result.setTimeZone(TimeZone.getTimeZone("GMT")); 584 result.setTimeInMillis((long)(Math.ceil(millis / 1000) * 1000)); 585 return result; 586 } 587 588 /** 589 * Splits a filter statement into a collection of properties. If 590 * <code>filter</code> is <code>null</code>, empty or one of the properties 591 * is '*' , an empty collection will be returned. 592 * 593 * @param filter the filter string 594 * @return the set of components of the filter 595 */ 596 public static Set<String> splitFilter(String filter) { 597 598 if (filter == null) { 599 return null; 600 } 601 602 if (filter.trim().length() == 0) { 603 return null; 604 } 605 606 Set<String> result = new LinkedHashSet<String>(); 607 for (String s : filter.split(",")) { 608 s = s.trim(); 609 if (s.equals("*")) { 610 return null; 611 } else if (s.length() > 0) { 612 result.add(s); 613 } 614 } 615 616 // set a few base properties 617 // query name == id (for base type properties) 618 result.add(PropertyIds.OBJECT_ID); 619 result.add(PropertyIds.OBJECT_TYPE_ID); 620 result.add(PropertyIds.BASE_TYPE_ID); 621 622 return result; 623 } 624 625}