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 static org.opencms.cmis.CmsCmisUtil.addAction;
031import static org.opencms.cmis.CmsCmisUtil.addPropertyBoolean;
032import static org.opencms.cmis.CmsCmisUtil.addPropertyDateTime;
033import static org.opencms.cmis.CmsCmisUtil.addPropertyId;
034import static org.opencms.cmis.CmsCmisUtil.addPropertyIdList;
035import static org.opencms.cmis.CmsCmisUtil.addPropertyInteger;
036import static org.opencms.cmis.CmsCmisUtil.addPropertyString;
037import static org.opencms.cmis.CmsCmisUtil.ensureLock;
038import static org.opencms.cmis.CmsCmisUtil.getAcePrincipalName;
039import static org.opencms.cmis.CmsCmisUtil.getCmisPermissions;
040import static org.opencms.cmis.CmsCmisUtil.getNativePermissions;
041import static org.opencms.cmis.CmsCmisUtil.handleCmsException;
042import static org.opencms.cmis.CmsCmisUtil.hasChildren;
043import static org.opencms.cmis.CmsCmisUtil.millisToCalendar;
044import static org.opencms.cmis.CmsCmisUtil.splitFilter;
045
046import org.opencms.file.CmsObject;
047import org.opencms.file.CmsProperty;
048import org.opencms.file.CmsResource;
049import org.opencms.file.CmsResourceFilter;
050import org.opencms.file.CmsUser;
051import org.opencms.file.types.I_CmsResourceType;
052import org.opencms.loader.CmsResourceManager;
053import org.opencms.lock.CmsLock;
054import org.opencms.main.CmsException;
055import org.opencms.main.OpenCms;
056import org.opencms.relations.CmsRelation;
057import org.opencms.relations.CmsRelationFilter;
058import org.opencms.security.CmsAccessControlEntry;
059import org.opencms.security.CmsPermissionSet;
060import org.opencms.util.CmsUUID;
061
062import java.util.ArrayList;
063import java.util.GregorianCalendar;
064import java.util.LinkedHashSet;
065import java.util.List;
066import java.util.Set;
067
068import org.apache.chemistry.opencmis.commons.PropertyIds;
069import org.apache.chemistry.opencmis.commons.data.Ace;
070import org.apache.chemistry.opencmis.commons.data.Acl;
071import org.apache.chemistry.opencmis.commons.data.AllowableActions;
072import org.apache.chemistry.opencmis.commons.data.ObjectData;
073import org.apache.chemistry.opencmis.commons.data.Properties;
074import org.apache.chemistry.opencmis.commons.data.RenditionData;
075import org.apache.chemistry.opencmis.commons.enums.Action;
076import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
077import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
078import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
079import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
080import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
081import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
082import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
083import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
084import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl;
085import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
086import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl;
087import org.apache.chemistry.opencmis.commons.impl.dataobjects.AllowableActionsImpl;
088import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
089import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
090import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
091import org.apache.chemistry.opencmis.commons.impl.server.RenditionInfoImpl;
092import org.apache.chemistry.opencmis.commons.server.RenditionInfo;
093
094/**
095 * Helper class for CRUD operations on resources.<p>
096 */
097public class CmsCmisResourceHelper implements I_CmsCmisObjectHelper {
098
099    /** The underlying repository. */
100    private CmsCmisRepository m_repository;
101
102    /**
103     * Creates a new instance.<p>
104     *
105     * @param repository the underlying repository
106     */
107    public CmsCmisResourceHelper(CmsCmisRepository repository) {
108
109        m_repository = repository;
110    }
111
112    /**
113     * Deletes a CMIS object.<p>
114     *
115     * @param context the call context
116     * @param objectId the id of the object to delete
117     * @param allVersions flag to delete all version
118     */
119    public synchronized void deleteObject(CmsCmisCallContext context, String objectId, boolean allVersions) {
120
121        try {
122            CmsObject cms = m_repository.getCmsObject(context);
123            CmsUUID structureId = new CmsUUID(objectId);
124            CmsResource resource = cms.readResource(structureId);
125            if (resource.isFolder()) {
126                boolean isLeaf = !hasChildren(cms, resource);
127                if (!isLeaf) {
128                    throw new CmisConstraintException("Only leaf resources can be deleted.");
129                }
130            }
131            ensureLock(cms, resource);
132            cms.deleteResource(resource.getRootPath(), CmsResource.DELETE_PRESERVE_SIBLINGS);
133        } catch (CmsException e) {
134            handleCmsException(e);
135        }
136    }
137
138    /**
139     * Gets the ACL for an object.<p>
140     *
141     * @param context the call context
142     * @param objectId the object id
143     * @param onlyBasicPermissions flag to only get basic permissions
144     *
145     * @return the ACL for the object
146     */
147    public synchronized Acl getAcl(CmsCmisCallContext context, String objectId, boolean onlyBasicPermissions) {
148
149        try {
150
151            CmsObject cms = m_repository.getCmsObject(context);
152            CmsUUID structureId = new CmsUUID(objectId);
153            CmsResource resource = cms.readResource(structureId);
154            return collectAcl(cms, resource, onlyBasicPermissions);
155        } catch (CmsException e) {
156            handleCmsException(e);
157            return null;
158        }
159
160    }
161
162    /**
163     * Gets the allowable actions for an object.<p>
164     *
165     * @param context the call context
166     * @param objectId the object id
167     * @return the allowable actions
168     */
169    public synchronized AllowableActions getAllowableActions(CmsCmisCallContext context, String objectId) {
170
171        try {
172            CmsObject cms = m_repository.getCmsObject(context);
173            CmsUUID structureId = new CmsUUID(objectId);
174            CmsResource file = cms.readResource(structureId);
175            return collectAllowableActions(cms, file);
176        } catch (CmsException e) {
177            handleCmsException(e);
178            return null;
179        }
180    }
181
182    /**
183     * Gets the data for a CMIS object.<p>
184     *
185     * @param context the CMIS call context
186     * @param objectId the id of the object
187     * @param filter the property filter
188     * @param includeAllowableActions flag to include allowable actions
189     * @param includeRelationships flag to include relationships
190     * @param renditionFilter the rendition filter string
191     * @param includePolicyIds flag to include policy ids
192     * @param includeAcl flag to include ACLs
193     *
194     * @return the CMIS object data
195     */
196    public synchronized ObjectData getObject(
197        CmsCmisCallContext context,
198        String objectId,
199        String filter,
200        boolean includeAllowableActions,
201        IncludeRelationships includeRelationships,
202        String renditionFilter,
203        boolean includePolicyIds,
204        boolean includeAcl) {
205
206        try {
207            //            if (renditionFilter.equals("cmis:none") && context.isObjectInfoRequired()) {
208            //                renditionFilter = "*";
209            //            }
210            // check id
211            if (objectId == null) {
212                throw new CmisInvalidArgumentException("Object Id must be set.");
213            }
214            CmsObject cms = m_repository.getCmsObject(context);
215            // get the file or folder
216            CmsResource file = cms.readResource(new CmsUUID(objectId));
217
218            // split filter
219            Set<String> filterCollection = splitFilter(filter);
220
221            // gather properties
222            return collectObjectData(
223                context,
224                cms,
225                file,
226                filterCollection,
227                renditionFilter,
228                includeAllowableActions,
229                includeAcl,
230                includeRelationships);
231        } catch (CmsException e) {
232            handleCmsException(e);
233            return null;
234        }
235    }
236
237    /**
238     * Compiles the ACL for a file or folder.
239     * @param cms the CMS context
240     * @param resource the resource for which to collect the ACLs
241     * @param onlyBasic flag to only include basic ACEs
242     *
243     * @return the ACL for the resource
244     * @throws CmsException if something goes wrong
245     */
246    protected Acl collectAcl(CmsObject cms, CmsResource resource, boolean onlyBasic) throws CmsException {
247
248        AccessControlListImpl cmisAcl = new AccessControlListImpl();
249        List<Ace> cmisAces = new ArrayList<Ace>();
250        List<CmsAccessControlEntry> aces = cms.getAccessControlEntries(resource.getRootPath(), true);
251        for (CmsAccessControlEntry ace : aces) {
252            boolean isDirect = ace.getResource().equals(resource.getResourceId());
253            CmsUUID principalId = ace.getPrincipal();
254            String principalName = getAcePrincipalName(cms, principalId);
255            AccessControlEntryImpl cmisAce = new AccessControlEntryImpl();
256            AccessControlPrincipalDataImpl cmisPrincipal = new AccessControlPrincipalDataImpl();
257            cmisPrincipal.setPrincipalId(principalName);
258            cmisAce.setPrincipal(cmisPrincipal);
259            cmisAce.setPermissions(onlyBasic ? getCmisPermissions(ace) : getNativePermissions(ace));
260            cmisAce.setDirect(isDirect);
261            cmisAces.add(cmisAce);
262        }
263        cmisAcl.setAces(cmisAces);
264        cmisAcl.setExact(Boolean.FALSE);
265        return cmisAcl;
266    }
267
268    /**
269     * Compiles the allowable actions for a file or folder.
270     *
271     * @param cms the current CMS context
272     * @param file the resource for which we want the allowable actions
273     *
274     * @return the allowable actions for the given resource
275     */
276    protected AllowableActions collectAllowableActions(CmsObject cms, CmsResource file) {
277
278        try {
279
280            if (file == null) {
281                throw new IllegalArgumentException("File must not be null!");
282            }
283            CmsLock lock = cms.getLock(file);
284            CmsUser user = cms.getRequestContext().getCurrentUser();
285            boolean canWrite = !cms.getRequestContext().getCurrentProject().isOnlineProject()
286                && (lock.isOwnedBy(user) || lock.isLockableBy(user))
287                && cms.hasPermissions(file, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.DEFAULT);
288            boolean isReadOnly = !canWrite;
289            boolean isFolder = file.isFolder();
290            boolean isRoot = file.getRootPath().length() <= 1;
291
292            Set<Action> aas = new LinkedHashSet<Action>();
293            addAction(aas, Action.CAN_GET_OBJECT_PARENTS, !isRoot);
294            addAction(aas, Action.CAN_GET_PROPERTIES, true);
295            addAction(aas, Action.CAN_UPDATE_PROPERTIES, !isReadOnly);
296            addAction(aas, Action.CAN_MOVE_OBJECT, !isReadOnly);
297            addAction(aas, Action.CAN_DELETE_OBJECT, !isReadOnly && !isRoot);
298            if (isFolder) {
299                addAction(aas, Action.CAN_GET_DESCENDANTS, true);
300                addAction(aas, Action.CAN_GET_CHILDREN, true);
301                addAction(aas, Action.CAN_GET_FOLDER_PARENT, !isRoot);
302                addAction(aas, Action.CAN_GET_FOLDER_TREE, true);
303                addAction(aas, Action.CAN_CREATE_DOCUMENT, !isReadOnly);
304                addAction(aas, Action.CAN_CREATE_FOLDER, !isReadOnly);
305                addAction(aas, Action.CAN_DELETE_TREE, !isReadOnly);
306            } else {
307                addAction(aas, Action.CAN_GET_CONTENT_STREAM, true);
308                addAction(aas, Action.CAN_SET_CONTENT_STREAM, !isReadOnly);
309                addAction(aas, Action.CAN_GET_ALL_VERSIONS, true);
310            }
311            AllowableActionsImpl result = new AllowableActionsImpl();
312            result.setAllowableActions(aas);
313            return result;
314        } catch (CmsException e) {
315            handleCmsException(e);
316            return null;
317        }
318    }
319
320    /**
321     * Fills in an ObjectData record.<p>
322     *
323     * @param context the call context
324     * @param cms the CMS context
325     * @param resource the resource for which we want the ObjectData
326     * @param filter the property filter
327     * @param renditionFilter the rendition filter string
328     * @param includeAllowableActions true if the allowable actions should be included
329     * @param includeAcl true if the ACL entries should be included
330     * @param includeRelationships true if relationships should be included
331     *
332     * @return the object data
333     * @throws CmsException if something goes wrong
334     */
335    protected ObjectData collectObjectData(
336        CmsCmisCallContext context,
337        CmsObject cms,
338        CmsResource resource,
339        Set<String> filter,
340        String renditionFilter,
341        boolean includeAllowableActions,
342        boolean includeAcl,
343        IncludeRelationships includeRelationships) throws CmsException {
344
345        ObjectDataImpl result = new ObjectDataImpl();
346        ObjectInfoImpl objectInfo = new ObjectInfoImpl();
347        result.setProperties(collectProperties(cms, resource, filter, objectInfo));
348
349        if (includeAllowableActions) {
350            result.setAllowableActions(collectAllowableActions(cms, resource));
351        }
352
353        if (includeAcl) {
354            result.setAcl(collectAcl(cms, resource, true));
355            result.setIsExactAcl(Boolean.FALSE);
356        }
357
358        if ((includeRelationships != null) && (includeRelationships != IncludeRelationships.NONE)) {
359            RelationshipDirection direction;
360            if (includeRelationships == IncludeRelationships.SOURCE) {
361                direction = RelationshipDirection.SOURCE;
362            } else if (includeRelationships == IncludeRelationships.TARGET) {
363                direction = RelationshipDirection.TARGET;
364            } else {
365                direction = RelationshipDirection.EITHER;
366            }
367
368            List<ObjectData> relationData = m_repository.getRelationshipObjectData(
369                context,
370                cms,
371                resource,
372                direction,
373                CmsCmisUtil.splitFilter("*"),
374                false);
375            result.setRelationships(relationData);
376        }
377
378        result.setRenditions(collectRenditions(cms, resource, renditionFilter, objectInfo));
379
380        if (context.isObjectInfoRequired()) {
381            objectInfo.setObject(result);
382            context.getObjectInfoHandler().addObjectInfo(objectInfo);
383        }
384        return result;
385    }
386
387    /**
388     * Gathers all base properties of a file or folder.
389     *
390     * @param cms the current CMS context
391     * @param resource the file for which we want the properties
392     * @param orgfilter the property filter
393     * @param objectInfo the object info handler
394     *
395     * @return the properties for the given resource
396     */
397    protected Properties collectProperties(
398        CmsObject cms,
399        CmsResource resource,
400        Set<String> orgfilter,
401        ObjectInfoImpl objectInfo) {
402
403        CmsCmisTypeManager tm = m_repository.getTypeManager();
404
405        if (resource == null) {
406            throw new IllegalArgumentException("Resource may not be null.");
407        }
408
409        // copy filter
410        Set<String> filter = (orgfilter == null ? null : new LinkedHashSet<String>(orgfilter));
411
412        // find base type
413        String typeId = null;
414
415        List<String> relationSourceIds = new ArrayList<String>();
416        List<String> relationTargetIds = new ArrayList<String>();
417        try {
418            List<CmsRelation> relations = cms.getRelationsForResource(resource, CmsRelationFilter.ALL);
419            for (CmsRelation relation : relations) {
420                if (resource.getStructureId().equals(relation.getSourceId())) {
421                    relationTargetIds.add(relation.getTargetId().toString());
422                }
423                if (resource.getStructureId().equals(relation.getTargetId())) {
424                    relationSourceIds.add(relation.getSourceId().toString());
425                }
426            }
427        } catch (CmsException e) {
428            throw new CmisStorageException(e.getLocalizedMessage(), e);
429        }
430
431        if (resource.isFolder()) {
432            typeId = CmsCmisTypeManager.FOLDER_TYPE_ID;
433            objectInfo.setBaseType(BaseTypeId.CMIS_FOLDER);
434            objectInfo.setTypeId(typeId);
435            objectInfo.setContentType(null);
436            objectInfo.setFileName(null);
437            objectInfo.setHasAcl(true);
438            objectInfo.setHasContent(false);
439            objectInfo.setVersionSeriesId(null);
440            objectInfo.setIsCurrentVersion(true);
441            objectInfo.setRelationshipSourceIds(relationSourceIds);
442            objectInfo.setRelationshipTargetIds(relationTargetIds);
443            objectInfo.setSupportsDescendants(true);
444            objectInfo.setSupportsFolderTree(true);
445            objectInfo.setSupportsPolicies(false);
446            objectInfo.setSupportsRelationships(true);
447            objectInfo.setWorkingCopyId(null);
448            objectInfo.setWorkingCopyOriginalId(null);
449        } else {
450            typeId = CmsCmisTypeManager.DOCUMENT_TYPE_ID;
451            objectInfo.setBaseType(BaseTypeId.CMIS_DOCUMENT);
452            objectInfo.setTypeId(typeId);
453            objectInfo.setHasAcl(true);
454            objectInfo.setHasContent(true);
455            objectInfo.setHasParent(true);
456            objectInfo.setVersionSeriesId(null);
457            objectInfo.setIsCurrentVersion(true);
458            objectInfo.setRelationshipSourceIds(relationSourceIds);
459            objectInfo.setRelationshipTargetIds(relationTargetIds);
460            objectInfo.setSupportsDescendants(false);
461            objectInfo.setSupportsFolderTree(false);
462            objectInfo.setSupportsPolicies(false);
463            objectInfo.setSupportsRelationships(true);
464            objectInfo.setWorkingCopyId(null);
465            objectInfo.setWorkingCopyOriginalId(null);
466        }
467        try {
468            PropertiesImpl result = new PropertiesImpl();
469
470            String id = resource.getStructureId().toString();
471            addPropertyId(tm, result, typeId, filter, PropertyIds.OBJECT_ID, id);
472            objectInfo.setId(id);
473
474            String name = resource.getName();
475            if ("".equals(name)) {
476                name = "/";
477            }
478            addPropertyString(tm, result, typeId, filter, PropertyIds.NAME, name);
479            objectInfo.setName(name);
480
481            // created and modified by
482            CmsUUID creatorId = resource.getUserCreated();
483            CmsUUID modifierId = resource.getUserLastModified();
484            String creatorName = creatorId.toString();
485            String modifierName = modifierId.toString();
486            try {
487                CmsUser user = cms.readUser(creatorId);
488                creatorName = user.getName();
489            } catch (CmsException e) {
490                // ignore, use id as name
491            }
492            try {
493                CmsUser user = cms.readUser(modifierId);
494                modifierName = user.getName();
495            } catch (CmsException e) {
496                // ignore, use id as name
497            }
498
499            addPropertyString(tm, result, typeId, filter, PropertyIds.CREATED_BY, creatorName);
500            addPropertyString(tm, result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, modifierName);
501            objectInfo.setCreatedBy(creatorName);
502
503            // creation and modification date
504            GregorianCalendar lastModified = millisToCalendar(resource.getDateLastModified());
505            GregorianCalendar created = millisToCalendar(resource.getDateCreated());
506
507            addPropertyDateTime(tm, result, typeId, filter, PropertyIds.CREATION_DATE, created);
508            addPropertyDateTime(tm, result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified);
509            objectInfo.setCreationDate(created);
510            objectInfo.setLastModificationDate(lastModified);
511
512            // change token - always null
513            addPropertyString(tm, result, typeId, filter, PropertyIds.CHANGE_TOKEN, null);
514
515            // directory or file
516            if (resource.isFolder()) {
517                // base type and type name
518                addPropertyId(tm, result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
519                addPropertyId(
520                    tm,
521                    result,
522                    typeId,
523                    filter,
524                    PropertyIds.OBJECT_TYPE_ID,
525                    CmsCmisTypeManager.FOLDER_TYPE_ID);
526                String path = resource.getRootPath();
527                addPropertyString(tm, result, typeId, filter, PropertyIds.PATH, (path.length() == 0 ? "/" : path));
528
529                // folder properties
530                if (resource.getRootPath().length() > 1) {
531                    CmsResource parent = cms.readParentFolder(resource.getStructureId());
532                    addPropertyId(tm, result, typeId, filter, PropertyIds.PARENT_ID, (
533
534                    parent.getStructureId().toString()));
535                    objectInfo.setHasParent(true);
536                } else {
537                    addPropertyId(tm, result, typeId, filter, PropertyIds.PARENT_ID, null);
538                    objectInfo.setHasParent(false);
539                }
540
541                addPropertyIdList(tm, result, typeId, filter, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, null);
542            } else {
543                // base type and type name
544                addPropertyId(tm, result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
545                addPropertyId(
546                    tm,
547                    result,
548                    typeId,
549                    filter,
550                    PropertyIds.OBJECT_TYPE_ID,
551                    CmsCmisTypeManager.DOCUMENT_TYPE_ID);
552
553                // file properties
554                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_IMMUTABLE, false);
555                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_LATEST_VERSION, true);
556                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_MAJOR_VERSION, true);
557                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, true);
558                addPropertyString(tm, result, typeId, filter, PropertyIds.VERSION_LABEL, resource.getName());
559                addPropertyId(
560                    tm,
561                    result,
562                    typeId,
563                    filter,
564                    PropertyIds.VERSION_SERIES_ID,
565                    resource.getStructureId().toString());
566                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, false);
567                addPropertyString(tm, result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null);
568                addPropertyString(tm, result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null);
569                addPropertyString(tm, result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "");
570                addPropertyInteger(tm, result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, resource.getLength());
571                addPropertyString(
572                    tm,
573                    result,
574                    typeId,
575                    filter,
576                    PropertyIds.CONTENT_STREAM_MIME_TYPE,
577                    OpenCms.getResourceManager().getMimeType(
578                        resource.getRootPath(),
579                        null,
580                        CmsResourceManager.MIMETYPE_TEXT));
581                addPropertyString(tm, result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, resource.getName());
582                objectInfo.setHasContent(true);
583                objectInfo.setContentType(
584                    OpenCms.getResourceManager().getMimeType(
585                        resource.getRootPath(),
586                        null,
587                        CmsResourceManager.MIMETYPE_TEXT));
588                objectInfo.setFileName(resource.getName());
589                addPropertyId(tm, result, typeId, filter, PropertyIds.CONTENT_STREAM_ID, null);
590            }
591            // normal OpenCms properties
592            List<CmsProperty> props = cms.readPropertyObjects(resource, false);
593            Set<String> propertiesToAdd = new LinkedHashSet<String>(
594                m_repository.getTypeManager().getCmsPropertyNames());
595            for (CmsProperty prop : props) {
596                addPropertyString(
597                    tm,
598                    result,
599                    typeId,
600                    filter,
601                    CmsCmisTypeManager.PROPERTY_PREFIX + prop.getName(),
602                    prop.getValue());
603                propertiesToAdd.remove(prop.getName());
604            }
605            for (String propName : propertiesToAdd) {
606                addPropertyString(tm, result, typeId, filter, CmsCmisTypeManager.PROPERTY_PREFIX + propName, null);
607            }
608
609            // inherited OpenCms properties
610            List<CmsProperty> inheritedProps = cms.readPropertyObjects(resource, true);
611            Set<String> inheritedPropertiesToAdd = new LinkedHashSet<String>(
612                m_repository.getTypeManager().getCmsPropertyNames());
613            for (CmsProperty prop : inheritedProps) {
614                addPropertyString(
615                    tm,
616                    result,
617                    typeId,
618                    filter,
619                    CmsCmisTypeManager.INHERITED_PREFIX + prop.getName(),
620                    prop.getValue());
621                inheritedPropertiesToAdd.remove(prop.getName());
622            }
623            for (String propName : inheritedPropertiesToAdd) {
624                addPropertyString(tm, result, typeId, filter, CmsCmisTypeManager.INHERITED_PREFIX + propName, null);
625            }
626
627            I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(resource);
628            addPropertyString(
629                tm,
630                result,
631                typeId,
632                filter,
633                CmsCmisTypeManager.PROPERTY_RESOURCE_TYPE,
634                resType.getTypeName());
635            CmsCmisUtil.addDynamicProperties(cms, m_repository.getTypeManager(), result, typeId, resource, filter);
636            return result;
637
638        } catch (Exception e) {
639            if (e instanceof CmisBaseException) {
640                throw (CmisBaseException)e;
641            }
642            throw new CmisRuntimeException(e.getMessage(), e);
643        }
644    }
645
646    /**
647     * Collects renditions for a resource.<p>
648     *
649     * @param cms the CMS context
650     * @param resource the resource for which we want the renditions
651     * @param renditionFilterString the filter string for the renditions
652     * @param objectInfo the object info in which the renditions should be saved
653     *
654     * @return the rendition data for the given resource
655     */
656    protected List<RenditionData> collectRenditions(
657        CmsObject cms,
658        CmsResource resource,
659        String renditionFilterString,
660        ObjectInfoImpl objectInfo) {
661
662        List<I_CmsCmisRenditionProvider> providers = m_repository.getRenditionProviders(
663            new CmsCmisRenditionFilter(renditionFilterString));
664        List<RenditionData> result = new ArrayList<RenditionData>();
665        List<RenditionInfo> renditionInfos = new ArrayList<RenditionInfo>();
666        for (I_CmsCmisRenditionProvider provider : providers) {
667            RenditionData renditionData = provider.getRendition(cms, resource);
668            if (renditionData != null) {
669                RenditionInfoImpl renditionInfo = new RenditionInfoImpl();
670                renditionInfo.setContentType(renditionData.getMimeType());
671                renditionInfo.setKind(renditionData.getKind());
672                renditionInfo.setId(renditionData.getStreamId());
673                result.add(renditionData);
674                renditionInfos.add(renditionInfo);
675            }
676        }
677        if (objectInfo != null) {
678            objectInfo.setRenditionInfos(renditionInfos);
679        }
680        return result;
681
682    }
683
684}