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.setId(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 && !isRoot);
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)
344    throws CmsException {
345
346        ObjectDataImpl result = new ObjectDataImpl();
347        ObjectInfoImpl objectInfo = new ObjectInfoImpl();
348        result.setProperties(collectProperties(cms, resource, filter, objectInfo));
349
350        if (includeAllowableActions) {
351            result.setAllowableActions(collectAllowableActions(cms, resource));
352        }
353
354        if (includeAcl) {
355            result.setAcl(collectAcl(cms, resource, true));
356            result.setIsExactAcl(Boolean.FALSE);
357        }
358
359        if ((includeRelationships != null) && (includeRelationships != IncludeRelationships.NONE)) {
360            RelationshipDirection direction;
361            if (includeRelationships == IncludeRelationships.SOURCE) {
362                direction = RelationshipDirection.SOURCE;
363            } else if (includeRelationships == IncludeRelationships.TARGET) {
364                direction = RelationshipDirection.TARGET;
365            } else {
366                direction = RelationshipDirection.EITHER;
367            }
368
369            List<ObjectData> relationData = m_repository.getRelationshipObjectData(
370                context,
371                cms,
372                resource,
373                direction,
374                CmsCmisUtil.splitFilter("*"),
375                false);
376            result.setRelationships(relationData);
377        }
378
379        result.setRenditions(collectRenditions(cms, resource, renditionFilter, objectInfo));
380
381        if (context.isObjectInfoRequired()) {
382            objectInfo.setObject(result);
383            context.getObjectInfoHandler().addObjectInfo(objectInfo);
384        }
385        return result;
386    }
387
388    /**
389     * Gathers all base properties of a file or folder.
390     *
391     * @param cms the current CMS context
392     * @param resource the file for which we want the properties
393     * @param orgfilter the property filter
394     * @param objectInfo the object info handler
395     *
396     * @return the properties for the given resource
397     */
398    protected Properties collectProperties(
399        CmsObject cms,
400        CmsResource resource,
401        Set<String> orgfilter,
402        ObjectInfoImpl objectInfo) {
403
404        CmsCmisTypeManager tm = m_repository.getTypeManager();
405
406        if (resource == null) {
407            throw new IllegalArgumentException("Resource may not be null.");
408        }
409
410        // copy filter
411        Set<String> filter = (orgfilter == null ? null : new LinkedHashSet<String>(orgfilter));
412
413        // find base type
414        String typeId = null;
415
416        List<String> relationSourceIds = new ArrayList<String>();
417        List<String> relationTargetIds = new ArrayList<String>();
418        try {
419            List<CmsRelation> relations = cms.getRelationsForResource(resource, CmsRelationFilter.ALL);
420            for (CmsRelation relation : relations) {
421                if (resource.getStructureId().equals(relation.getSourceId())) {
422                    relationTargetIds.add(relation.getTargetId().toString());
423                }
424                if (resource.getStructureId().equals(relation.getTargetId())) {
425                    relationSourceIds.add(relation.getSourceId().toString());
426                }
427            }
428        } catch (CmsException e) {
429            throw new CmisStorageException(e.getLocalizedMessage(), e);
430        }
431
432        if (resource.isFolder()) {
433            typeId = CmsCmisTypeManager.FOLDER_TYPE_ID;
434            objectInfo.setBaseType(BaseTypeId.CMIS_FOLDER);
435            objectInfo.setTypeId(typeId);
436            objectInfo.setContentType(null);
437            objectInfo.setFileName(null);
438            objectInfo.setHasAcl(true);
439            objectInfo.setHasContent(false);
440            objectInfo.setVersionSeriesId(null);
441            objectInfo.setIsCurrentVersion(true);
442            objectInfo.setRelationshipSourceIds(relationSourceIds);
443            objectInfo.setRelationshipTargetIds(relationTargetIds);
444            objectInfo.setSupportsDescendants(true);
445            objectInfo.setSupportsFolderTree(true);
446            objectInfo.setSupportsPolicies(false);
447            objectInfo.setSupportsRelationships(true);
448            objectInfo.setWorkingCopyId(null);
449            objectInfo.setWorkingCopyOriginalId(null);
450        } else {
451            typeId = CmsCmisTypeManager.DOCUMENT_TYPE_ID;
452            objectInfo.setBaseType(BaseTypeId.CMIS_DOCUMENT);
453            objectInfo.setTypeId(typeId);
454            objectInfo.setHasAcl(true);
455            objectInfo.setHasContent(true);
456            objectInfo.setHasParent(true);
457            objectInfo.setVersionSeriesId(null);
458            objectInfo.setIsCurrentVersion(true);
459            objectInfo.setRelationshipSourceIds(relationSourceIds);
460            objectInfo.setRelationshipTargetIds(relationTargetIds);
461            objectInfo.setSupportsDescendants(false);
462            objectInfo.setSupportsFolderTree(false);
463            objectInfo.setSupportsPolicies(false);
464            objectInfo.setSupportsRelationships(true);
465            objectInfo.setWorkingCopyId(null);
466            objectInfo.setWorkingCopyOriginalId(null);
467        }
468        try {
469            PropertiesImpl result = new PropertiesImpl();
470
471            String id = resource.getStructureId().toString();
472            addPropertyId(tm, result, typeId, filter, PropertyIds.OBJECT_ID, id);
473            objectInfo.setId(id);
474
475            String name = resource.getName();
476            if ("".equals(name)) {
477                name = "/";
478            }
479            addPropertyString(tm, result, typeId, filter, PropertyIds.NAME, name);
480            objectInfo.setName(name);
481
482            // created and modified by
483            CmsUUID creatorId = resource.getUserCreated();
484            CmsUUID modifierId = resource.getUserLastModified();
485            String creatorName = creatorId.toString();
486            String modifierName = modifierId.toString();
487            try {
488                CmsUser user = cms.readUser(creatorId);
489                creatorName = user.getName();
490            } catch (CmsException e) {
491                // ignore, use id as name
492            }
493            try {
494                CmsUser user = cms.readUser(modifierId);
495                modifierName = user.getName();
496            } catch (CmsException e) {
497                // ignore, use id as name
498            }
499
500            addPropertyString(tm, result, typeId, filter, PropertyIds.CREATED_BY, creatorName);
501            addPropertyString(tm, result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, modifierName);
502            objectInfo.setCreatedBy(creatorName);
503
504            // creation and modification date
505            GregorianCalendar lastModified = millisToCalendar(resource.getDateLastModified());
506            GregorianCalendar created = millisToCalendar(resource.getDateCreated());
507
508            addPropertyDateTime(tm, result, typeId, filter, PropertyIds.CREATION_DATE, created);
509            addPropertyDateTime(tm, result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified);
510            objectInfo.setCreationDate(created);
511            objectInfo.setLastModificationDate(lastModified);
512
513            // change token - always null
514            addPropertyString(tm, result, typeId, filter, PropertyIds.CHANGE_TOKEN, null);
515
516            // directory or file
517            if (resource.isFolder()) {
518                // base type and type name
519                addPropertyId(tm, result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
520                addPropertyId(
521                    tm,
522                    result,
523                    typeId,
524                    filter,
525                    PropertyIds.OBJECT_TYPE_ID,
526                    CmsCmisTypeManager.FOLDER_TYPE_ID);
527                String path = resource.getRootPath();
528                addPropertyString(tm, result, typeId, filter, PropertyIds.PATH, (path.length() == 0 ? "/" : path));
529
530                // folder properties
531                if (resource.getRootPath().length() > 1) {
532                    CmsResource parent = cms.readParentFolder(resource.getStructureId());
533                    addPropertyId(tm, result, typeId, filter, PropertyIds.PARENT_ID, (
534
535                    parent.getStructureId().toString()));
536                    objectInfo.setHasParent(true);
537                } else {
538                    addPropertyId(tm, result, typeId, filter, PropertyIds.PARENT_ID, null);
539                    objectInfo.setHasParent(false);
540                }
541
542                addPropertyIdList(tm, result, typeId, filter, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, null);
543            } else {
544                // base type and type name
545                addPropertyId(tm, result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
546                addPropertyId(
547                    tm,
548                    result,
549                    typeId,
550                    filter,
551                    PropertyIds.OBJECT_TYPE_ID,
552                    CmsCmisTypeManager.DOCUMENT_TYPE_ID);
553
554                // file properties
555                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_IMMUTABLE, false);
556                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_LATEST_VERSION, true);
557                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_MAJOR_VERSION, true);
558                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, true);
559                addPropertyString(tm, result, typeId, filter, PropertyIds.VERSION_LABEL, resource.getName());
560                addPropertyId(
561                    tm,
562                    result,
563                    typeId,
564                    filter,
565                    PropertyIds.VERSION_SERIES_ID,
566                    resource.getStructureId().toString());
567                addPropertyBoolean(tm, result, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, false);
568                addPropertyString(tm, result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null);
569                addPropertyString(tm, result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null);
570                addPropertyString(tm, result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "");
571                addPropertyInteger(tm, result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, resource.getLength());
572                addPropertyString(
573                    tm,
574                    result,
575                    typeId,
576                    filter,
577                    PropertyIds.CONTENT_STREAM_MIME_TYPE,
578                    OpenCms.getResourceManager().getMimeType(
579                        resource.getRootPath(),
580                        null,
581                        CmsResourceManager.MIMETYPE_TEXT));
582                addPropertyString(tm, result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, resource.getName());
583                objectInfo.setHasContent(true);
584                objectInfo.setContentType(
585                    OpenCms.getResourceManager().getMimeType(
586                        resource.getRootPath(),
587                        null,
588                        CmsResourceManager.MIMETYPE_TEXT));
589                objectInfo.setFileName(resource.getName());
590                addPropertyId(tm, result, typeId, filter, PropertyIds.CONTENT_STREAM_ID, null);
591            }
592            // normal OpenCms properties
593            List<CmsProperty> props = cms.readPropertyObjects(resource, false);
594            Set<String> propertiesToAdd = new LinkedHashSet<String>(
595                m_repository.getTypeManager().getCmsPropertyNames());
596            for (CmsProperty prop : props) {
597                addPropertyString(
598                    tm,
599                    result,
600                    typeId,
601                    filter,
602                    CmsCmisTypeManager.PROPERTY_PREFIX + prop.getName(),
603                    prop.getValue());
604                propertiesToAdd.remove(prop.getName());
605            }
606            for (String propName : propertiesToAdd) {
607                addPropertyString(tm, result, typeId, filter, CmsCmisTypeManager.PROPERTY_PREFIX + propName, null);
608            }
609
610            // inherited OpenCms properties
611            List<CmsProperty> inheritedProps = cms.readPropertyObjects(resource, true);
612            Set<String> inheritedPropertiesToAdd = new LinkedHashSet<String>(
613                m_repository.getTypeManager().getCmsPropertyNames());
614            for (CmsProperty prop : inheritedProps) {
615                addPropertyString(
616                    tm,
617                    result,
618                    typeId,
619                    filter,
620                    CmsCmisTypeManager.INHERITED_PREFIX + prop.getName(),
621                    prop.getValue());
622                inheritedPropertiesToAdd.remove(prop.getName());
623            }
624            for (String propName : inheritedPropertiesToAdd) {
625                addPropertyString(tm, result, typeId, filter, CmsCmisTypeManager.INHERITED_PREFIX + propName, null);
626            }
627
628            I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(resource);
629            addPropertyString(
630                tm,
631                result,
632                typeId,
633                filter,
634                CmsCmisTypeManager.PROPERTY_RESOURCE_TYPE,
635                resType.getTypeName());
636            CmsCmisUtil.addDynamicProperties(cms, m_repository.getTypeManager(), result, typeId, resource, filter);
637            return result;
638
639        } catch (Exception e) {
640            if (e instanceof CmisBaseException) {
641                throw (CmisBaseException)e;
642            }
643            throw new CmisRuntimeException(e.getMessage(), e);
644        }
645    }
646
647    /**
648     * Collects renditions for a resource.<p>
649     *
650     * @param cms the CMS context
651     * @param resource the resource for which we want the renditions
652     * @param renditionFilterString the filter string for the renditions
653     * @param objectInfo the object info in which the renditions should be saved
654     *
655     * @return the rendition data for the given resource
656     */
657    protected List<RenditionData> collectRenditions(
658        CmsObject cms,
659        CmsResource resource,
660        String renditionFilterString,
661        ObjectInfoImpl objectInfo) {
662
663        List<I_CmsCmisRenditionProvider> providers = m_repository.getRenditionProviders(
664            new CmsCmisRenditionFilter(renditionFilterString));
665        List<RenditionData> result = new ArrayList<RenditionData>();
666        List<RenditionInfo> renditionInfos = new ArrayList<RenditionInfo>();
667        for (I_CmsCmisRenditionProvider provider : providers) {
668            RenditionData renditionData = provider.getRendition(cms, resource);
669            if (renditionData != null) {
670                RenditionInfoImpl renditionInfo = new RenditionInfoImpl();
671                renditionInfo.setContentType(renditionData.getMimeType());
672                renditionInfo.setKind(renditionData.getKind());
673                renditionInfo.setId(renditionData.getStreamId());
674                result.add(renditionData);
675                renditionInfos.add(renditionInfo);
676            }
677        }
678        if (objectInfo != null) {
679            objectInfo.setRenditionInfos(renditionInfos);
680        }
681        return result;
682
683    }
684
685}